单个枚举与多个过滤器

时间:2015-04-25 02:15:08

标签: c# linq

鉴于以下设想的例子:

public bool NumberOfEvensEqualsNumberOfOdds(IEnumerable<int> numbers) {
    var numberOfEvens = numbers.Count(x => x % 2 == 0);
    var numberOfOdds = numbers.Count(x => x % 2 != 0);

    return numberOfEvens == numberOfOdds;
}

这有效,但需要多次枚举该集合。

是否可以重写它以使用枚举集合的单个linq表达式。

注意:我正在尝试解决将计数与两个过滤器进行比较的一般情况,因此请尝试忽略样本与奇数和偶数相关的事实。

我添加了一个示例.NET Fiddle

5 个答案:

答案 0 :(得分:2)

您可以使用all_sights = params[:sights].split(',')

GroupBy

答案 1 :(得分:2)

乍一看有点神秘,但只能在集合中迭代一次。

Func<int, bool> isEven = n => n % 2 == 0;
Func<int, bool> isFive = n => n == 5;
int diff = numbers.Aggregate(0, (sum, next) => isEven(next) ? sum + 1 : isFive(next) ? sum - 1 : sum);

对于集合中的每个项目,它会检查这两个条件。如果第一个条件适用,它会向聚合变量添加一个;如果第二个适用,它会减去一个。最终结果是满足第一个标准的项目数与满足第二个标准的项目数之间的差异。

答案 2 :(得分:2)

如果你有两个不相交的过滤器(也就是说,一个项目不能同时满足这两个过滤器),那么BJ Myer的答案可能就像你能得到的那样简单有效。

如果你有两个不一定不相交的过滤器,那么你可以使用以下微小的变化,它总是评估每个项目的两个过滤器:

public static bool NumberOfEvensEqualsNumberOfOdds(IEnumerable<int> numbers)
{
    // Compute
    //   numbers.Count(x => x % 2 == 0)       - numbers.Count(x => x % 2 != 0)
    // or equivalently,
    //   numbers.Sum(x => x % 2 == 0 ? 1 : 0) - numbers.Sum(x => x % 2 != 0 ? 1 : 0)
    int sum = numbers.Sum(x =>
        (x % 2 == 0 ? 1 : 0) -
        (x % 2 != 0 ? 1 : 0));
    return sum == 0;
}

如果你有任意数量的不一定不相交的过滤器,那么你可以使用以下通用方法:

public static bool HasEqualSizeSubsets<T>(
    IEnumerable<T> items, params Func<T, bool>[] filters)
{
    var indexedFilters = filters
        .Select((filter, index) => new { Filter = filter, Index = index })
        .ToArray(); // to avoid repeated object allocations later
    IEnumerable<int> subsetSizes = items
        .SelectMany(item => indexedFilters
            .Where(indexedFilter => indexedFilter.Filter(item))
            .Select(indexedFilter => indexedFilter.Index))
        .GroupBy(index => index)
        .Select(grouping => grouping.Count());
    return subsetSizes.Distinct().Count() == 1;
}

HasEqualSizeSubsets看起来很复杂,但基本思路很简单:

  • 首先,我们获取filters数组参数中传递的每个过滤器的数组索引。
  • 然后,对于item中的每个items,我们得到item满足的每个过滤器的索引。 (例如,如果item仅满足第一个过滤器,则输出“0”。如果item满足前两个过滤器,则输出“0”和“1”。)结果SelectMany调用是一系列过滤器索引。
  • 接下来,我们计算每个索引出现的次数。 GroupBy和Select调用的结果是一系列子集大小。
  • 最后,我们检查所有子集大小是否都是相同的唯一值。

HasEqualSizeSubsets可以像这样使用:

public static bool NumberOfEvensEqualsNumberOfOdds(IEnumerable<int> numbers)
{
    return HasEqualSizeSubsets(numbers, x => x % 2 == 0, x => x % 2 != 0);
}

答案 3 :(得分:1)

您使用.Aggregate代码执行此操作:

public bool NumberOfEvensEqualsNumberOfOdds(IEnumerable<int> numbers)
{
    var result =
        numbers
            .Aggregate(
                new { evens = 0, odds = 0 },
                (a, x) =>
                {
                    a = x % 2 == 0
                        ? new { evens = a.evens + 1, a.odds }
                        : a;
                    a = x % 2 != 0 
                        ? new { a.evens, odds = a.odds + 1 }
                        : a;
                    return a;
                });

    return result.evens == result.odds;
}

可以更新逻辑以计算来自源编号的不同投影的数量。

答案 4 :(得分:0)

如果您想检查两种不同的条件并仅循环一次。 你可以使用foreach循环并在

中拥有自己的逻辑
Sub split_and_create()
    Dim rw As Long, lr As Long, lc As Long, v As Long, vSTATs As Variant, vREGNs As Variant

    With ActiveSheet
        lr = .Cells(Rows.Count, 1).End(xlUp).Row
        lc = .Cells(1, Columns.Count).End(xlToLeft).Column
        .Cells(1, 2).CurrentRegion.Rows(1).Copy _
            Destination:=.Cells(lr + 2, 1)
        For rw = 2 To lr
            vSTATs = Application.Index(.Cells(rw, 1).Resize(1, lc).Value, 1, 0)
            vREGNs = Split(vSTATs(4), " - ")
            For v = LBound(vREGNs) To UBound(vREGNs)
                vSTATs(4) = vREGNs(v)
                .Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Resize(1, lc) = vSTATs
            Next v
        Next rw
    End With
End Sub