如何使用LINQ比较char数组的元素?

时间:2018-07-18 05:01:28

标签: c# linq

假设我有一个像这样的数组:

char[] l = {'a', 'a', 'b', 'c'}

现在,我要完成的任务是将数组的第一个元素与第二个元素进行比较,如果它们匹配,则向前移动(将第二个元素与第三个元素进行比较,依此类推),如果它们不匹配,则取与下一个不匹配的第一个元素。做类似这样的事情:

var l = l.TakeTheFirstOne((x, y) => x != y)

在这种情况下,答案应该是位于第二位置的字符'a'

3 个答案:

答案 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();

使用IEnumerator

如果仔细观察,您会发现序列的第一个元素被访问了两次(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;
                }
            }
        }
    }
}

具有KeySelector,ElementSelector和EqualityComparer的完整版本

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
}

一旦有了这个,让其他重载都称为这个。