简体   繁体   English

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

[英]How to compare the elements of an char array using LINQ?

Let's say I have an array like this: 假设我有一个这样的数组:

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

Now, what i want to accomplish is to compare the first element of the array with the second one, move forward (compare second one with third, and so on) if they match, but if they don't then take the first element that doesn't match with the next one. 现在,我想要完成的是将数组的第一个元素与第二个元素进行比较,如果它们匹配则向前移动(将第二个元素与第三个元素进行比较,依此类推),但如果它们不匹配,则采用第一个元素与下一个不匹配。 Doing something similar like this: 做类似这样的事情:

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

In this case the answer should be the char 'a' at second position. 在这种情况下,答案应该是第二个位置的char'a 'a'

var answer = l.Select((item, index) => new {item, index})
         .FirstOrDefault(x => (x.index != l.Length - 1) && x.item != l[x.index + 1])?.item;

Based on the select, this version will iterate over the matching items of the collection without the count condition. 基于select,此版本将迭代集合的匹配项而不计算条件。

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;

If efficiency is important: use Yield return in an extension function 如果效率很重要:在扩展函数中使用Yield return

Although the solution with the GroupBy / Select / Last / FirstOrDefault / Last might works, it is easy to see that the sequence is enumerated several times. 尽管使用GroupBy / Select / Last / FirstOrDefault / Last的解决方案可能有效,但很容易看到序列被多次枚举。

Whenever you think that LINQ is missing a function, why not extend IEnumerable with your desired function? 每当你认为LINQ缺少一个函数时,为什么不用所需的函数扩展IEnumerable See 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;
            }
        }
    }
}

Usage: 用法:

char[] items = ...;
IEnumerable<char> differentItems = items.TakeFirstOne();

Using IEnumerator 使用IEnumerator

If you look closely, you'll see that the first element of the sequence is accessed two times(once for the FirstOrDefault and once for the Skip ), all other elements are accessed exactly once. 如果你仔细观察,你会看到序列的第一个元素被访问两次(一次用于FirstOrDefault ,一次用于Skip ),所有其他元素只被访问一次。

If you want to optimize this even further, you'll have to enumerate yourself and remember how far you already have enumerated: 如果你想进一步优化它,你必须列举自己,并记住你已经枚举了多远:

Version that enumerates exactly once 只列举一次的版本

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;
                }
            }
        }
    }
}

Full version with KeySelector, ElementSelector and EqualityComparer 带KeySelector,ElementSelector和EqualityComparer的完整版本

TODO: Consider to add a version that takes a KeySelector (which property to check), an IEqualityComparer (when are two Keys equal?) and ElementSelector (which properties to return if the Keys are unequal?). TODO:考虑添加一个带KeySelector(要检查哪个属性)的版本,IEqualityComparer(两个Keys何时相等?)和ElementSelector(如果Keys不相等则返回哪些属性?)。 As an example use Enumerable.ToDictionary 例如,使用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
}

Once you've got this one, let your other overloads call this one. 一旦你有了这个,让你的其他重载调用这个。

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

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