4

假设我有一个可枚举的来源,如下所示:

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

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

"first", "first", "second"

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

4

5 回答 5

10
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)))

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

于 2012-05-22T21:09:41.627 回答
8

我认为 Amy 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"}

于 2012-08-15T20:08:18.753 回答
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”参数(将 > 1 更改为 > numberToRemove)。YAGNI 和所有这些,但它的可读性与布尔值一样。

于 2012-05-23T12:05:11.843 回答
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 语句,但是已经晚了,我的大脑无法正常工作!

于 2012-05-22T21:17:37.737 回答
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}

希望这可以帮助!

于 2012-05-22T21:10:07.100 回答