假设我有一个像这样的数组:
char[] l = {'a', 'a', 'b', 'c'}
现在,我要完成的任务是将数组的第一个元素与第二个元素进行比较,如果它们匹配,则向前移动(将第二个元素与第三个元素进行比较,依此类推),如果它们不匹配,则取与下一个不匹配的第一个元素。做类似这样的事情:
var l = l.TakeTheFirstOne((x, y) => x != y)
在这种情况下,答案应该是位于第二位置的字符'a'
。
答案 0 :(得分:1)
var answer = l.Select((item, index) => new {item, index})
.FirstOrDefault(x => (x.index != l.Length - 1) && x.item != l[x.index + 1])?.item;
答案 1 :(得分:0)
基于选择,此版本将迭代集合中匹配的项目,而没有计数条件。
char[] items = {'a', 'a', 'b', 'c'};
var position = items
.Skip(1)
.Select((character, index) => new {character, index})
.SkipWhile(t => t.character == items[t.index])
.FirstOrDefault()
?.index;
答案 2 :(得分:0)
如果效率很重要:在扩展函数中使用收益率回报
尽管使用GroupBy / Select / Last / FirstOrDefault / Last的解决方案可能有效,但很容易看到该序列被枚举了几次。
每当您认为LINQ缺少功能时,为何不使用所需的功能扩展IEnumerable
?参见Extension Methods demystified
// from a sequence, takes the first one that is unequal to the previous one
// and skips all following equal ones
// example: 2 2 2 5 4 4 2 will result in: 2 5 4 2
public static IEnumerable<TSource> TakeFirstOne<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
TSource firstDifferentItem = source.FirstOrDefault();
if (firstDifferentItem != null)
{ // at least one item in the sequence
// return the first item
yield return firstDifferentItem;
// enumerate the other items, until you find a different one
foreach (TSource nextItem in source.Skip(1))
{
// if nextItem not equal: yield return
if (!firstDifferentItem.Equals(nextItem))
{
firstDifferentItem = nextItem;
yield return firstDifferentItem;
}
}
}
}
用法:
char[] items = ...;
IEnumerable<char> differentItems = items.TakeFirstOne();
如果仔细观察,您会发现序列的第一个元素被访问了两次(FirstOrDefault
一次,Skip
一次),所有其他元素恰好被访问一次
如果您想进一步优化它,则必须枚举自己并记住您已经枚举的距离:
仅枚举一次的版本
public static IEnumerable<TSource> TakeFirstOne<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
if (enumerator.MoveNext())
{ // there is at least one element in the section.
TSource firstDifferentItem = enumerator.Current;
yield return firstDifferentItem;
// continue enumerating:
while(enumerator.MoveNext())
{
// if Current not equal: yield return
if (!firstDifferentItem.Equals(enumerator.Current))
{
firstDifferentItem = enumerator.Current;
yield return firstDifferentItem;
}
}
}
}
}
TODO:考虑添加一个版本,该版本带有一个KeySelector(要检查的属性),一个IEqualityComparer(何时两个键相等)和ElementSelector(如果键不相等,则返回哪个属性?)。例如,使用Enumerable.ToDictionary
public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TSource, TElement> elementSelector,
IEqualityComparer<TKey> comparer
{
// select the key from the first element
// yield return elementSelector(sourceItem)
// check all other elements: get the key, if equal: skip. else:
// remember key and yield return elementSelector(sourceItem)
// use comparer to check for equal key
// use comparer == null, use EqualityComparer<TKey>.Default
}
一旦有了这个,让其他重载都称为这个。