LINQ 查询按某些条件将有序列表拆分为连续点的子列表

发布于 2024-12-05 05:04:18 字数 622 浏览 0 评论 0原文

寻求针对某些对象编写 LINQ 查询的帮助。我觉得如果我的 LINQ 技能更像忍者,我可以使用一些聪明的 GroupBy/SelectMany (或其他东西?!) 来做到这一点。

一般而言,问题是:给定按某种顺序排列的对象列表,其中每个对象都有一个标志,如何将列表拆分为子列表,其中每个子列表是设置标志的所有连续点?

执行此操作的命令式方法类似于以下伪代码:

foreach object obj
  if(obj.FlagSet) 
    add it to my currentsublist
  else
    skip to the next obj where FlagSet and start a new sublist

因此,给定以下输入:

{ 1, Flag }, { 2, Flag }, {3, NoFlag }, {4, Flag}, {5, NoFlag}, {6, Flag}...

我想要以下输出:

列表 1:{1, 2} 列表 2:{4} 列表 3:{6}

我想通过 LINQ 来实现它的功能。有什么想法吗?

(我首先环顾四周,但我看到的所有问题似乎都想简单地对列表进行分组或分成相等的大小,这对我没有帮助。)

Looking for help writing a LINQ query on some objects. I feel if my LINQ skills were more ninja I could do this with some clever GroupBy/SelectMany (or something?!).

Stated generically, the question is: given a list of objects in some sort of order, where each object has a Flag, how do I split the list into sub-lists, where each sublist is all of the contiguous points where the flag is set?

An imperative way of doing this would be like the following pseudocode:

foreach object obj
  if(obj.FlagSet) 
    add it to my currentsublist
  else
    skip to the next obj where FlagSet and start a new sublist

So, given the following input:

{ 1, Flag }, { 2, Flag }, {3, NoFlag }, {4, Flag}, {5, NoFlag}, {6, Flag}...

I'd like the following output:

List 1: {1, 2}
List 2: {4}
List 3: {6}

And I'd like to do it functionally via LINQ. Any ideas?

(I have looked around first, but all the questions I could see appeared to want to either simply group a list or to split into equal sizes, which hasn't been helpful for me.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

动次打次papapa 2024-12-12 05:04:18

此 MSDN 文章提供了按连续值进行分组的代码:

http://msdn.microsoft。 com/en-us/library/cc138361.aspx

我已经从上面的链接复制了代码,以防链接腐烂:

public static class MyExtensions
{
    public static IEnumerable<IGrouping<TKey, TSource>> ChunkBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
    {
        return source.ChunkBy(keySelector, EqualityComparer<TKey>.Default);
    }

    public static IEnumerable<IGrouping<TKey, TSource>> ChunkBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
    {
        // Flag to signal end of source sequence.
        const bool noMoreSourceElements = true;

        // Auto-generated iterator for the source array.       
        var enumerator = source.GetEnumerator();

        // Move to the first element in the source sequence.
        if (!enumerator.MoveNext()) yield break;

        // Iterate through source sequence and create a copy of each Chunk.
        // On each pass, the iterator advances to the first element of the next "Chunk"
        // in the source sequence. This loop corresponds to the outer foreach loop that
        // executes the query.
        Chunk<TKey, TSource> current = null;
        while (true)
        {
            // Get the key for the current Chunk. The source iterator will churn through
            // the source sequence until it finds an element with a key that doesn't match.
            var key = keySelector(enumerator.Current);

            // Make a new Chunk (group) object that initially has one GroupItem, which is a copy of the current source element.
            current = new Chunk<TKey, TSource>(key, enumerator, value => comparer.Equals(key, keySelector(value)));

            // Return the Chunk. A Chunk is an IGrouping<TKey,TSource>, which is the return value of the ChunkBy method.
            // At this point the Chunk only has the first element in its source sequence. The remaining elements will be
            // returned only when the client code foreach's over this chunk. See Chunk.GetEnumerator for more info.
            yield return current;

            // Check to see whether (a) the chunk has made a copy of all its source elements or 
            // (b) the iterator has reached the end of the source sequence. If the caller uses an inner
            // foreach loop to iterate the chunk items, and that loop ran to completion,
            // then the Chunk.GetEnumerator method will already have made
            // copies of all chunk items before we get here. If the Chunk.GetEnumerator loop did not
            // enumerate all elements in the chunk, we need to do it here to avoid corrupting the iterator
            // for clients that may be calling us on a separate thread.
            if (current.CopyAllChunkElements() == noMoreSourceElements)
            {
                yield break;
            }
        }
    }

    // A Chunk is a contiguous group of one or more source elements that have the same key. A Chunk 
    // has a key and a list of ChunkItem objects, which are copies of the elements in the source sequence.
    class Chunk<TKey, TSource> : IGrouping<TKey, TSource>
    {
        // INVARIANT: DoneCopyingChunk == true || 
        //   (predicate != null && predicate(enumerator.Current) && current.Value == enumerator.Current)

        // A Chunk has a linked list of ChunkItems, which represent the elements in the current chunk. Each ChunkItem
        // has a reference to the next ChunkItem in the list.
        class ChunkItem
        {
            public ChunkItem(TSource value)
            {
                Value = value;
            }
            public readonly TSource Value;
            public ChunkItem Next = null;
        }
        // The value that is used to determine matching elements
        private readonly TKey key;

        // Stores a reference to the enumerator for the source sequence
        private IEnumerator<TSource> enumerator;

        // A reference to the predicate that is used to compare keys.
        private Func<TSource, bool> predicate;

        // Stores the contents of the first source element that
        // belongs with this chunk.
        private readonly ChunkItem head;

        // End of the list. It is repositioned each time a new
        // ChunkItem is added.
        private ChunkItem tail;

        // Flag to indicate the source iterator has reached the end of the source sequence.
        internal bool isLastSourceElement = false;

        // Private object for thread syncronization
        private object m_Lock;

        // REQUIRES: enumerator != null && predicate != null
        public Chunk(TKey key, IEnumerator<TSource> enumerator, Func<TSource, bool> predicate)
        {
            this.key = key;
            this.enumerator = enumerator;
            this.predicate = predicate;

            // A Chunk always contains at least one element.
            head = new ChunkItem(enumerator.Current);

            // The end and beginning are the same until the list contains > 1 elements.
            tail = head;

            m_Lock = new object();
        }

        // Indicates that all chunk elements have been copied to the list of ChunkItems, 
        // and the source enumerator is either at the end, or else on an element with a new key.
        // the tail of the linked list is set to null in the CopyNextChunkElement method if the
        // key of the next element does not match the current chunk's key, or there are no more elements in the source.
        private bool DoneCopyingChunk { get { return tail == null; } }

        // Adds one ChunkItem to the current group
        // REQUIRES: !DoneCopyingChunk && lock(this)
        private void CopyNextChunkElement()
        {
            // Try to advance the iterator on the source sequence.
            // If MoveNext returns false we are at the end, and isLastSourceElement is set to true
            isLastSourceElement = !enumerator.MoveNext();

            // If we are (a) at the end of the source, or (b) at the end of the current chunk
            // then null out the enumerator and predicate for reuse with the next chunk.
            if (isLastSourceElement || !predicate(enumerator.Current))
            {
                enumerator = null;
                predicate = null;
            }
            else
            {
                tail.Next = new ChunkItem(enumerator.Current);
            }

            // tail will be null if we are at the end of the chunk elements
            // This check is made in DoneCopyingChunk.
            tail = tail.Next;
        }

        // Called after the end of the last chunk was reached. It first checks whether
        // there are more elements in the source sequence. If there are, it 
        // Returns true if enumerator for this chunk was exhausted.
        internal bool CopyAllChunkElements()
        {
            while (true)
            {
                lock (m_Lock)
                {
                    if (DoneCopyingChunk)
                    {
                        // If isLastSourceElement is false,
                        // it signals to the outer iterator
                        // to continue iterating.
                        return isLastSourceElement;
                    }
                    else
                    {
                        CopyNextChunkElement();
                    }
                }
            }
        }

        public TKey Key { get { return key; } }

        // Invoked by the inner foreach loop. This method stays just one step ahead
        // of the client requests. It adds the next element of the chunk only after
        // the clients requests the last element in the list so far.
        public IEnumerator<TSource> GetEnumerator()
        {
            //Specify the initial element to enumerate.
            ChunkItem current = head;

            // There should always be at least one ChunkItem in a Chunk.
            while (current != null)
            {
                // Yield the current item in the list.
                yield return current.Value;

                // Copy the next item from the source sequence, 
                // if we are at the end of our local list.
                lock (m_Lock)
                {
                    if (current == tail)
                    {
                        CopyNextChunkElement();
                    }
                }

                // Move to the next ChunkItem in the list.
                current = current.Next;
            }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}

它不漂亮,但效果很好。

在你的情况下,它会是这样的:

myList.ChunkBy( o => o.FlagSet )

This MSDN article provides code to group by contiguous values:

http://msdn.microsoft.com/en-us/library/cc138361.aspx

I've reproduced the code from the link above in case of link-rot:

public static class MyExtensions
{
    public static IEnumerable<IGrouping<TKey, TSource>> ChunkBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
    {
        return source.ChunkBy(keySelector, EqualityComparer<TKey>.Default);
    }

    public static IEnumerable<IGrouping<TKey, TSource>> ChunkBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
    {
        // Flag to signal end of source sequence.
        const bool noMoreSourceElements = true;

        // Auto-generated iterator for the source array.       
        var enumerator = source.GetEnumerator();

        // Move to the first element in the source sequence.
        if (!enumerator.MoveNext()) yield break;

        // Iterate through source sequence and create a copy of each Chunk.
        // On each pass, the iterator advances to the first element of the next "Chunk"
        // in the source sequence. This loop corresponds to the outer foreach loop that
        // executes the query.
        Chunk<TKey, TSource> current = null;
        while (true)
        {
            // Get the key for the current Chunk. The source iterator will churn through
            // the source sequence until it finds an element with a key that doesn't match.
            var key = keySelector(enumerator.Current);

            // Make a new Chunk (group) object that initially has one GroupItem, which is a copy of the current source element.
            current = new Chunk<TKey, TSource>(key, enumerator, value => comparer.Equals(key, keySelector(value)));

            // Return the Chunk. A Chunk is an IGrouping<TKey,TSource>, which is the return value of the ChunkBy method.
            // At this point the Chunk only has the first element in its source sequence. The remaining elements will be
            // returned only when the client code foreach's over this chunk. See Chunk.GetEnumerator for more info.
            yield return current;

            // Check to see whether (a) the chunk has made a copy of all its source elements or 
            // (b) the iterator has reached the end of the source sequence. If the caller uses an inner
            // foreach loop to iterate the chunk items, and that loop ran to completion,
            // then the Chunk.GetEnumerator method will already have made
            // copies of all chunk items before we get here. If the Chunk.GetEnumerator loop did not
            // enumerate all elements in the chunk, we need to do it here to avoid corrupting the iterator
            // for clients that may be calling us on a separate thread.
            if (current.CopyAllChunkElements() == noMoreSourceElements)
            {
                yield break;
            }
        }
    }

    // A Chunk is a contiguous group of one or more source elements that have the same key. A Chunk 
    // has a key and a list of ChunkItem objects, which are copies of the elements in the source sequence.
    class Chunk<TKey, TSource> : IGrouping<TKey, TSource>
    {
        // INVARIANT: DoneCopyingChunk == true || 
        //   (predicate != null && predicate(enumerator.Current) && current.Value == enumerator.Current)

        // A Chunk has a linked list of ChunkItems, which represent the elements in the current chunk. Each ChunkItem
        // has a reference to the next ChunkItem in the list.
        class ChunkItem
        {
            public ChunkItem(TSource value)
            {
                Value = value;
            }
            public readonly TSource Value;
            public ChunkItem Next = null;
        }
        // The value that is used to determine matching elements
        private readonly TKey key;

        // Stores a reference to the enumerator for the source sequence
        private IEnumerator<TSource> enumerator;

        // A reference to the predicate that is used to compare keys.
        private Func<TSource, bool> predicate;

        // Stores the contents of the first source element that
        // belongs with this chunk.
        private readonly ChunkItem head;

        // End of the list. It is repositioned each time a new
        // ChunkItem is added.
        private ChunkItem tail;

        // Flag to indicate the source iterator has reached the end of the source sequence.
        internal bool isLastSourceElement = false;

        // Private object for thread syncronization
        private object m_Lock;

        // REQUIRES: enumerator != null && predicate != null
        public Chunk(TKey key, IEnumerator<TSource> enumerator, Func<TSource, bool> predicate)
        {
            this.key = key;
            this.enumerator = enumerator;
            this.predicate = predicate;

            // A Chunk always contains at least one element.
            head = new ChunkItem(enumerator.Current);

            // The end and beginning are the same until the list contains > 1 elements.
            tail = head;

            m_Lock = new object();
        }

        // Indicates that all chunk elements have been copied to the list of ChunkItems, 
        // and the source enumerator is either at the end, or else on an element with a new key.
        // the tail of the linked list is set to null in the CopyNextChunkElement method if the
        // key of the next element does not match the current chunk's key, or there are no more elements in the source.
        private bool DoneCopyingChunk { get { return tail == null; } }

        // Adds one ChunkItem to the current group
        // REQUIRES: !DoneCopyingChunk && lock(this)
        private void CopyNextChunkElement()
        {
            // Try to advance the iterator on the source sequence.
            // If MoveNext returns false we are at the end, and isLastSourceElement is set to true
            isLastSourceElement = !enumerator.MoveNext();

            // If we are (a) at the end of the source, or (b) at the end of the current chunk
            // then null out the enumerator and predicate for reuse with the next chunk.
            if (isLastSourceElement || !predicate(enumerator.Current))
            {
                enumerator = null;
                predicate = null;
            }
            else
            {
                tail.Next = new ChunkItem(enumerator.Current);
            }

            // tail will be null if we are at the end of the chunk elements
            // This check is made in DoneCopyingChunk.
            tail = tail.Next;
        }

        // Called after the end of the last chunk was reached. It first checks whether
        // there are more elements in the source sequence. If there are, it 
        // Returns true if enumerator for this chunk was exhausted.
        internal bool CopyAllChunkElements()
        {
            while (true)
            {
                lock (m_Lock)
                {
                    if (DoneCopyingChunk)
                    {
                        // If isLastSourceElement is false,
                        // it signals to the outer iterator
                        // to continue iterating.
                        return isLastSourceElement;
                    }
                    else
                    {
                        CopyNextChunkElement();
                    }
                }
            }
        }

        public TKey Key { get { return key; } }

        // Invoked by the inner foreach loop. This method stays just one step ahead
        // of the client requests. It adds the next element of the chunk only after
        // the clients requests the last element in the list so far.
        public IEnumerator<TSource> GetEnumerator()
        {
            //Specify the initial element to enumerate.
            ChunkItem current = head;

            // There should always be at least one ChunkItem in a Chunk.
            while (current != null)
            {
                // Yield the current item in the list.
                yield return current.Value;

                // Copy the next item from the source sequence, 
                // if we are at the end of our local list.
                lock (m_Lock)
                {
                    if (current == tail)
                    {
                        CopyNextChunkElement();
                    }
                }

                // Move to the next ChunkItem in the list.
                current = current.Next;
            }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}

It's not pretty, but works well.

In your case it would be something like:

myList.ChunkBy( o => o.FlagSet )
酸甜透明夹心 2024-12-12 05:04:18

除了 @spenders 优秀链接(+1!) 之外,我还要补充一点

:漂亮,而且运行良好:

  • 它完全在惰性模式下运行,
  • 它是线程安全的,
  • 通过提供实现 IGroupingChunk 集成到标准 linq
  • 中一些风格问题(命名、超出范围、缺少 m_lock 的只读修饰符;诸如此类的事情)

我现在看到的唯一真正抱怨是它无法主动处理从源获取的枚举器可数。这是我的相关修复:

public static IEnumerable<IGrouping<TKey, TSource>> ChunkBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
    //  ...
    using (var enumerator = source.GetEnumerator()) // <--- FIXED
    {

更新

这是我完全修改的源代码,修复了上面列出的所有问题。 **这也使得 Chunk 成为一次性的:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ChunkIt
{
    public static class MyExtensions
    {
        public static IEnumerable<IGrouping<TKey, TSource>> ChunkBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
        {
            return source.ChunkBy(keySelector, EqualityComparer<TKey>.Default);
        }

        public static IEnumerable<IGrouping<TKey, TSource>> ChunkBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
        {
            const bool noMoreSourceElements = true;

            using (var enumerator = source.GetEnumerator())
            {
                if (!enumerator.MoveNext()) 
                    yield break;

                Chunk<TKey, TSource> current;
                while (true)
                {
                    var key = keySelector(enumerator.Current);
                    current = new Chunk<TKey, TSource>(key, enumerator, value => comparer.Equals(key, keySelector(value)));

                    yield return current;

                    if (current.CopyAllChunkElements() == noMoreSourceElements)
                        yield break;
                }
            }
        }

        class Chunk<TKey, TSource> : IGrouping<TKey, TSource>, IDisposable
        {
            class ChunkItem
            {
                public ChunkItem(TSource value)
                {
                    Value = value;
                }
                public readonly TSource Value;
                public ChunkItem Next;
            }

            private readonly TKey _key;
            private IEnumerator<TSource> _enumerator;
            private Func<TSource, bool> _predicate;
            private readonly ChunkItem _head;
            private ChunkItem _tail;
            private bool _isLastSourceElement;
            private readonly object _mLock;

            public Chunk(TKey key, IEnumerator<TSource> enumerator, Func<TSource, bool> predicate)
            {
                _key = key;
                _enumerator = enumerator;
                _predicate = predicate;

                _head = new ChunkItem(enumerator.Current);

                _tail = _head;

                _mLock = new object();
            }

            private bool DoneCopyingChunk { get { return _tail == null; } }

            private void CopyNextChunkElement()
            {
                _isLastSourceElement = !_enumerator.MoveNext();

                if (_isLastSourceElement || !_predicate(_enumerator.Current))
                {
                    _enumerator = null;
                    _predicate = null;
                }
                else
                {
                    _tail.Next = new ChunkItem(_enumerator.Current);
                }

                _tail = _tail.Next;
            }

            internal bool CopyAllChunkElements()
            {
                while (true)
                    lock (_mLock)
                    {
                        if (DoneCopyingChunk)
                            return _isLastSourceElement;

                        CopyNextChunkElement();
                    }
            }

            public TKey Key { get { return _key; } }

            public IEnumerator<TSource> GetEnumerator()
            {
                ChunkItem current = _head;

                while (current != null)
                {
                    yield return current.Value;

                    lock (_mLock)
                        if (current == _tail)
                            CopyNextChunkElement();

                    current = current.Next;
                }
            }

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }

            #region Implementation of IDisposable

            public void Dispose()
            {
                if (null!=_enumerator)
                    _enumerator.Dispose();

            }

            #endregion
        }
    }
}

In addition to @spenders excellent link (+1!), I'd add:

It is pretty, and it works well:

  • it operates fully in lazy mode
  • it is thread safe
  • integrates into standard linq by providing Chunk<> which implements IGrouping<>
  • it has some style issues (naming, excess scope, missing readonly modifier for m_lock; things like that)

The only real gripe I saw right now was that it fails to pro-actively dispose the enumerators it gets from the source enumerable. Here is my relevant fix:

public static IEnumerable<IGrouping<TKey, TSource>> ChunkBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
    //  ...
    using (var enumerator = source.GetEnumerator()) // <--- FIXED
    {

Update

Here is my completely amended source, fixes all the issues listed above. **This also makes Chunk<> disposable:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ChunkIt
{
    public static class MyExtensions
    {
        public static IEnumerable<IGrouping<TKey, TSource>> ChunkBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
        {
            return source.ChunkBy(keySelector, EqualityComparer<TKey>.Default);
        }

        public static IEnumerable<IGrouping<TKey, TSource>> ChunkBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
        {
            const bool noMoreSourceElements = true;

            using (var enumerator = source.GetEnumerator())
            {
                if (!enumerator.MoveNext()) 
                    yield break;

                Chunk<TKey, TSource> current;
                while (true)
                {
                    var key = keySelector(enumerator.Current);
                    current = new Chunk<TKey, TSource>(key, enumerator, value => comparer.Equals(key, keySelector(value)));

                    yield return current;

                    if (current.CopyAllChunkElements() == noMoreSourceElements)
                        yield break;
                }
            }
        }

        class Chunk<TKey, TSource> : IGrouping<TKey, TSource>, IDisposable
        {
            class ChunkItem
            {
                public ChunkItem(TSource value)
                {
                    Value = value;
                }
                public readonly TSource Value;
                public ChunkItem Next;
            }

            private readonly TKey _key;
            private IEnumerator<TSource> _enumerator;
            private Func<TSource, bool> _predicate;
            private readonly ChunkItem _head;
            private ChunkItem _tail;
            private bool _isLastSourceElement;
            private readonly object _mLock;

            public Chunk(TKey key, IEnumerator<TSource> enumerator, Func<TSource, bool> predicate)
            {
                _key = key;
                _enumerator = enumerator;
                _predicate = predicate;

                _head = new ChunkItem(enumerator.Current);

                _tail = _head;

                _mLock = new object();
            }

            private bool DoneCopyingChunk { get { return _tail == null; } }

            private void CopyNextChunkElement()
            {
                _isLastSourceElement = !_enumerator.MoveNext();

                if (_isLastSourceElement || !_predicate(_enumerator.Current))
                {
                    _enumerator = null;
                    _predicate = null;
                }
                else
                {
                    _tail.Next = new ChunkItem(_enumerator.Current);
                }

                _tail = _tail.Next;
            }

            internal bool CopyAllChunkElements()
            {
                while (true)
                    lock (_mLock)
                    {
                        if (DoneCopyingChunk)
                            return _isLastSourceElement;

                        CopyNextChunkElement();
                    }
            }

            public TKey Key { get { return _key; } }

            public IEnumerator<TSource> GetEnumerator()
            {
                ChunkItem current = _head;

                while (current != null)
                {
                    yield return current.Value;

                    lock (_mLock)
                        if (current == _tail)
                            CopyNextChunkElement();

                    current = current.Next;
                }
            }

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }

            #region Implementation of IDisposable

            public void Dispose()
            {
                if (null!=_enumerator)
                    _enumerator.Dispose();

            }

            #endregion
        }
    }
}
饭团 2024-12-12 05:04:18

我认为你管理两个列表的方法是
过去只需将第一个列表添加到最终列表并清除第一个列表
并在设置标志时将对象添加到第一个列表

I think the way is you manage two list
and on else past just add first list to final list and clear first list
and add object to first list when flag is set

痴情 2024-12-12 05:04:18

通过 LINQ

var list = new[]
{
    new KeyValuePair<string, string>("A", "We"),
    new KeyValuePair<string, string>("A", "Think"),
    new KeyValuePair<string, string>("A", "That"),
    new KeyValuePair<string, string>("B", "Linq"),
    new KeyValuePair<string, string>("C", "Is"),
    new KeyValuePair<string, string>("A", "Really"),
    new KeyValuePair<string, string>("B", "Cool"),
    new KeyValuePair<string, string>("B", "!")
};
var queryGroup = list.Select(data => data).
    GroupBy(g => g.Key, //group key
            i => i.Value //value
    );
foreach (var group in queryGroup)
{
    foreach (var data in group)
    {

    }
}
//even/odd grouping
int groupCount = 2;
var queryEvenOdd = list.Select((data, index) => new { data, index }).
        GroupBy(
        g => g.index % groupCount,//group key
        i => i.data //value
        );

By LINQ

var list = new[]
{
    new KeyValuePair<string, string>("A", "We"),
    new KeyValuePair<string, string>("A", "Think"),
    new KeyValuePair<string, string>("A", "That"),
    new KeyValuePair<string, string>("B", "Linq"),
    new KeyValuePair<string, string>("C", "Is"),
    new KeyValuePair<string, string>("A", "Really"),
    new KeyValuePair<string, string>("B", "Cool"),
    new KeyValuePair<string, string>("B", "!")
};
var queryGroup = list.Select(data => data).
    GroupBy(g => g.Key, //group key
            i => i.Value //value
    );
foreach (var group in queryGroup)
{
    foreach (var data in group)
    {

    }
}
//even/odd grouping
int groupCount = 2;
var queryEvenOdd = list.Select((data, index) => new { data, index }).
        GroupBy(
        g => g.index % groupCount,//group key
        i => i.data //value
        );
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文