当项目相同时从可枚举源中删除单个项目

时间:2012-05-22 20:59:07

标签: c# linq ienumerable

假设我有一个可枚举的来源,看起来像这样:

IEnumerable<string> source = new [] { "first", "first", "first", "second" };

我希望能够构造一个返回它的LINQ语句:

"first", "first", "second"

注意只有一个第一次消失了。我不关心哪一个,因为在我的情况下,所有3个“第一个”被认为是平等的。我已经尝试了source.Except(new [] { "first" })但是将所有实例删除了。

5 个答案:

答案 0 :(得分:8)

source
  .GroupBy(s => s)
  .SelectMany(g => g.Skip(1).DefaultIfEmpty(g.First()))

对于每个组,跳过组的第一个元素并返回其余组 - 除非这将返回无...在这种情况下,返回组的第一个元素。


source
  .GroupBy(s => s)
  .SelectMany(g => g.Take(1).Concat(g.Skip(2)))

对于每个组,取第一个元素,然后从第三个元素开始 - 总是跳过第二个元素。

答案 1 :(得分:6)

我认为David B的回答让你非常接近,但是在那里只有一个值的情况下它不会删除它,这就是我认为原始海报所寻找的。

这是一个扩展方法,它将删除所请求项的单个实例,即使这是最后一个实例。这反映了LINQ Except()调用,但只删除了第一个实例,而不是所有实例。

    public static IEnumerable<T> ExceptSingle<T>(this IEnumerable<T> source, T valueToRemove)
    {
        return source
            .GroupBy(s => s)
            .SelectMany(g => g.Key.Equals(valueToRemove) ? g.Skip(1) : g);
    }

鉴于:{"one", "two", "three", "three", "three"}
致电source.ExceptSingle("three")会产生{"one", "two", "three", "three"}

鉴于:{"one", "two", "three", "three"}
致电source.ExceptSingle("three")会产生{"one", "two", "three"}

鉴于:{"one", "two", "three"}
致电source.ExceptSingle("three")会产生{"one", "two"}

鉴于:{"one", "two", "three", "three"}
致电source.ExceptSingle("four")会产生{"one", "two", "three", "three"}

答案 2 :(得分:3)

我想出了一个单线LINQ语句来做到这一点。它需要一个单独的标志变量。我将其实现为扩展方法:

public static IEnumerable<T> ExceptOne<T>(this IEnumerable<T> enumerable, T element)
{
    var i = 0;

    return enumerable.Where(original => !EqualityComparer<T>.Default.Equals(original, element) || ++i > 1);
}

我使用了int,以防我后来想要添加“numberToRemove”参数(将&gt; 1更改为&gt; numberToRemove)。 YAGNI和所有这一切,但无论如何它都像布尔一样可读。

答案 3 :(得分:1)

IEnumerable<string> source = new [] { "first", "first", "first", "second" };

List<string> newSource = new List<string>();

var foo = source.GroupBy (s => s).Select (s => new KeyValuePair<string,int>(s.Key, (s.Count()>1)?s.Count()-1:s.Count ()));

foreach (var element in foo)
{
    newSource.AddRange(Enumerable.Repeat(element.Key,element.Value));
}

这里非常努力。基本上,这将创建原始的第二个列表,每个不同的密钥和实例的数量,如果有多个,则扣除一个,然后重新渲染具有正确数量的元素的列表。

不如大卫B的回答那么优雅,但我已经写过了,但我也可以将其作为另一个可能的答案发布。我确信foreach可以用于Linq语句,但是已经很晚了,我的大脑不能正常工作!

答案 4 :(得分:0)

我对LINQ并不是很熟悉,但这里有一些你可能想要使用的流程:

将所有独特项目存储在新列表B中,即:

A: {1, 1, 1, 2, 4, 4, 6}

变为

B: {1, 2, 4, 6}

通过B迭代,如果存在,则删除A中的实例,即:。

A: {1, 1, 1, 2, 4, 4, 6}

变为

F: {1, 1, 2, 4, 6}

希望这有帮助!