简体   繁体   English

Linq查询将重复项的子集减少到更大集合中的单个值?

[英]Linq query that reduces a subset of duplicates to a single value within a larger set?

Is there a linq command that will filter out duplicates that appear in a sequence? 是否有一个linq命令可以过滤掉序列中出现的重复项?

Example with '4': '4'的示例:

Original { 1 2 3 4 4 4 5 6 7 4 4 4 8 9 4 4 4 }
Filtered { 1 2 3 4 5 6 7 4 8 9 4 }

Thanks. 谢谢。

Not really. 并不是的。 I'd write this: 我写这个:

public static IEnumerable<T> RemoveDuplicates(this IEnumerable<T> sequence)
{
    bool init = false;
    T current = default(T);

    foreach (var x in sequence)
    {
        if (!init || !object.Equals(current, x))
            yield return x;

        current = x;
        init = true;
    }   
}

Yes there is! 就在这里! One-line code and one loop of the array. 单行代码和数组的一个循环。

int[] source = new int[] { 1, 2, 3, 4, 4, 4, 5, 6, 7, 4, 4, 4, 8, 9, 4, 4, 4 };
var result = source.Where((item, index) => index + 1 == source.Length 
                          || item != source[index + 1]);

And according to @Hogan's advice, it can be better: 根据@ Hogan的建议,它可以更好:

var result = source.Where((item, index) => index == 0 
                          || item != source[index - 1]);

More readable now i think. 我认为现在更具可读性。 It means "choose the first element, and those which isn't equal to the previous one". 它意味着“选择第一个元素,以及那些不等于前一个元素的元素”。

Similar to svick's answer, except with side effects to avoid the cons and reverse: 与svick的答案类似,除了副作用以避免缺点和逆转:

int[] source = new int[] { 1, 2, 3, 4, 4, 4, 5, 6, 7, 4, 4, 4, 8, 9, 4, 4, 4 };

List<int> result = new List<int> { source.First() };
source.Aggregate((acc, c) =>
    {
        if (acc != c)
            result.Add(c);
        return c;
    });

Edit: No longer needs the source.First() as per mquander's concern: 编辑:根据mquander的关注,不再需要source.First source.First()

int[] source = new int[] { 1, 2, 3, 4, 4, 4, 5, 6, 7, 4, 4, 4, 8, 9, 4, 4, 4 };

List<int> result = new List<int>();
result.Add(
    source.Aggregate((acc, c) =>
    {
        if (acc != c)
            result.Add(acc);
        return c;
    })
);

I think I still like Danny's solution the most. 我想我仍然最喜欢Danny的解决方案。

You can use Aggregate() (although I'm not sure whether it's better than the non-LINQ solution): 您可以使用Aggregate() (虽然我不确定它是否比非LINQ解决方案更好):

var ints = new[] { 1, 2, 3, 4, 4, 4, 5, 6, 7, 4, 4, 4, 8, 9, 4, 4, 4 };

var result = ints.Aggregate(
    Enumerable.Empty<int>(),
    (list, i) =>
        list.Any() && list.First() == i
        ? list
        : new[] { i }.Concat(list)).Reverse();

I think it's O( n ), but I'm not completely sure. 认为这是O( n ),但我不完全确定。

If you're using .NET 4 then you can do this using the built-in Zip method, although I'd probably prefer to use a custom extension method like the one shown in mquander's answer . 如果您正在使用.NET 4,那么您可以使用内置的Zip方法执行此操作,尽管我可能更喜欢使用自定义扩展方法,如mquander的答案中所示。

// replace "new int[1]" below with "new T[1]" depending on the type of element
var filtered = original.Zip(new int[1].Concat(original),
                            (l, r) => new { L = l, R = r })
                       .Where((x, i) => (i == 0) || !object.Equals(x.L, x.R))
                       .Select(x => x.L);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM