【问题标题】:How to split an array into a group of n elements each?如何将一个数组分成一组n个元素?
【发布时间】:2011-03-31 16:39:42
【问题描述】:

在 c# 4 中,将数组分组为包含 n 个元素的数组列表的最佳方法是什么。

例如

string[] testArray = { "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8" };

如果取n=3,应该拆分成。

string[] A1 = {"s1", "s2", "s3"};
string[] A2 = {"s4", "s5", "s6"};
string[] A3 = {"s7", "s8"};

可能是使用 LINQ 的简单方法?

【问题讨论】:

    标签: c# linq arrays .net-4.0


    【解决方案1】:

    这将生成一个包含 3 个元素的字符串数组:

    int i = 0;
    var query = from s in testArray
                let num = i++
                group s by num / 3 into g
                select g.ToArray();
    var results = query.ToArray();
    

    【讨论】:

    • +1,这种方法的唯一缺点是它被急切地评估。必须先处理整个查询,然后才能返回单个元素。
    • @JaredPar:点很好;但是,根据集合的大小或处理的性质,惰性求值可能被高估了。即便如此,为您提供有效的惰性方法的解决方案 +1。
    【解决方案2】:

    我不认为有一个很好的内置方法,但你可以写一个像下面这样的。

    public static IEnumerable<IEnumerable<T>> GroupInto<T>(
      this IEnumerable<T> source,
      int count) {
    
      using ( var e = source.GetEnumerator() ) {
        while ( e.MoveNext() ) { 
          yield return GroupIntoHelper(e, count);
        }
      }    
    }
    
    private static IEnumerable<T> GroupIntoHelper<T>(
      IEnumerator<T> e,
      int count) {
    
      do {
        yield return e.Current;
        count--;
      } while ( count > 0 && e.MoveNext());
    }
    

    【讨论】:

    • 完美答案。但应该提供一个例子来说明它如何解决问题中的问题。
    【解决方案3】:
    int size = 3;
    var results = testArray.Select((x, i) => new { Key = i / size, Value = x })
                           .GroupBy(x => x.Key, x => x.Value, (k, g) => g.ToArray())
                           .ToArray();
    

    如果您不介意将结果键入为 IEnumerable&lt;IEnumerable&lt;T&gt;&gt; 而不是 T[][],那么您可以完全省略 ToArray 调用:

    int size = 3;
    var results = testArray.Select((x, i) => new { Key = i / size, Value = x })
                           .GroupBy(x => x.Key, x => x.Value);
    

    【讨论】:

      【解决方案4】:

      如果您正在使用的实际上是数组而不是一般的 IEnumerables,特别是如果数组非常大,那么这种方法是一种非常快速且内存有效的方法。如果你真的只是想要一个 LINQ 语句,那么没关系。

          private static T[][] SliceArray<T>(T[] source, int maxResultElements)
          {
              int numberOfArrays = source.Length / maxResultElements;
              if (maxResultElements * numberOfArrays < source.Length)
                  numberOfArrays++;
              T[][] target = new T[numberOfArrays][];
              for (int index = 0; index < numberOfArrays; index++)
              {
                  int elementsInThisArray = Math.Min(maxResultElements, source.Length - index * maxResultElements);
                  target[index] = new T[elementsInThisArray];
                  Array.Copy(source, index * maxResultElements, target[index], 0, elementsInThisArray);
              }
              return target;
          }
      

      【讨论】:

        【解决方案5】:

        你可以使用这个扩展

        public static class Extension
        {
            private static IEnumerable<TList> Split<TList, T>(this TList value, int countOfEachPart) where TList : IEnumerable<T>
            {
                int cnt = value.Count() / countOfEachPart;
                List<IEnumerable<T>> result = new List<IEnumerable<T>>();
                for (int i = 0; i <= cnt; i++)
                {
                    IEnumerable<T> newPart = value.Skip(i * countOfEachPart).Take(countOfEachPart).ToArray();
                    if (newPart.Any())
                        result.Add(newPart);
                    else
                        break;
                }
        
                return result.Cast<TList>();
            }
        
            public static IEnumerable<IDictionary<TKey, TValue>> Split<TKey, TValue>(this IDictionary<TKey, TValue> value, int countOfEachPart)
            {
                IEnumerable<Dictionary<TKey, TValue>> result = value.ToArray()
                                                                    .Split(countOfEachPart)
                                                                    .Select(p => p.ToDictionary(k => k.Key, v => v.Value));
                return result;
            }
        
            public static IEnumerable<IList<T>> Split<T>(this IList<T> value, int countOfEachPart)
            {
                return value.Split<IList<T>, T>(countOfEachPart);
            }
        
            public static IEnumerable<T[]> Split<T>(this T[] value, int countOfEachPart)
            {
                return value.Split<T[], T>(countOfEachPart);
            }
        
            public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> value, int countOfEachPart)
            {
                return value.Split<IEnumerable<T>, T>(countOfEachPart);
            }
        }
        

        【讨论】:

          【解决方案6】:

          MoreLinq提供Batch扩展方法

          【讨论】:

            猜你喜欢
            • 2012-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-12-05
            • 2022-12-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多