合并具有可变长度的多个列表"弹出"每个元素

时间:2016-11-23 15:27:19

标签: c# sorting merge

我想将多个列表(可变数量的列表)排序到单个列表中,但保留特定的顺序。例如:

List A: { 1,2,3,4,5 }
List B: { 6,7,8 }
List C: { 9,10,11,12 }

Result List: { 1,6,9,2,7,10,3,8,11,4,12,5 }

我得到的唯一想法是从每个列表中删除第一个元素并将其放入结果集中(并重复直到所有列表都为空),但也许有一种更好的方法不需要创建副本每个列表,并不会影响原始列表?

7 个答案:

答案 0 :(得分:5)

我建议使用IEnumerator<T>在列表中枚举列表:

private static IEnumerable<T> Merge<T>(params IEnumerable<T>[] sources) {
  List<IEnumerator<T>> enums = sources
    .Select(source => source.GetEnumerator())
    .ToList();

  try {
    while (enums.Any()) {
      for (int i = 0; i < enums.Count;)
        if (enums[i].MoveNext()) {
          yield return enums[i].Current;

          i += 1;
        }
        else {
          // exhausted, let's remove enumerator
          enums[i].Dispose();
          enums.RemoveAt(i);
        }
    }
  }
  finally {
    foreach (var en in enums)
      en.Dispose();
  }
}

测试

List<int> A = new List<int>() { 1, 2, 3, 4, 5 };
List<int> B = new List<int>() { 6, 7, 8 };
List<int> C = new List<int>() { 9, 10, 11, 12 };

var result = Merge(A, B, C)
  .ToList();

Console.Write(string.Join(", ", result));

结果是

1, 6, 9, 2, 7, 10, 3, 8, 11, 4, 12, 5

答案 1 :(得分:2)

在我看来,为什么不使用一个简单的for循环来完成你需要的东西,这没有任何意义呢?

List<int> list1 = new List<int> { 1, 2, 3, 4, 5 };
List<int> list2 = new List<int> { 6, 7, 8 };
List<int> list3 = new List<int> { 9, 10, 11, 12 };
List<int> resultList = new List<int>();

for (int i = 0; i < list1.Count || i < list2.Count || i < list3.Count; i++)
{
    if (i < list1.Count) resultList.Add(list1[i]);
    if (i < list2.Count) resultList.Add(list2[i]);
    if (i < list3.Count) resultList.Add(list3[i]);
}

结果:1​​,6,9,2,7,10,3,8,11,4,12,5

答案 2 :(得分:2)

更灵活的使用

 public static string MergeArrays(params IList<int>[] items)
    {

        var result = new List<int>();
        for (var i = 0; i < items.Max(x => x.Count); i++)
            result.AddRange(from rowList in items where rowList.Count > i select rowList[i]);

        return string.Join(",", result);
    }

        var a = new List<int>() { 1, 2, 3, 4, 5 };
        var b = new List<int>() { 6, 7, 8 };
        var c = new List<int>() { 9, 10, 11, 12, 0, 2, 1 };

        var r = MergeArrays(a, b, c);

答案 3 :(得分:1)

这是一种相当简单的方法。无论如何写起来很有趣。
不,它不是最好的,但它可以工作,你可以扩展它,以满足你很容易使用List<List<int>>的需要。

//Using arrays for simplicity, you get the idea.
int[] A = { 1, 2, 3, 4, 5 };
int[] B = { 6, 7, 8 };
int[] C = { 9, 10, 11, 12 };

List<int> ResultSet = new List<int>();

//Determine this somehow. I'm doing this for simplicity.
int longest = 5; 

for (int i = 0; i < longest; i++)
{
    if (i < A.Length)
        ResultSet.Add(A[i]);
    if (i < B.Length)
        ResultSet.Add(B[i]);
    if (i < C.Length)
        ResultSet.Add(C[i]);
}

//ResultSet contains: { 1, 6, 9, 2, 7, 10, 3, 8, 11, 4, 12, 5 }

正如您所看到的,只需将其弹出到一个方法中并遍历列表列表,正确确定所有列表的最大长度。

答案 4 :(得分:0)

最短且可能最慢的解决方案

int[] A = { 1, 2, 3, 4, 5 };
int[] B = { 6, 7, 8 };
int[] C = { 9, 10, 11, 12 };


var arrs = new[] { A, B, C };
var merged = Enumerable.Range(0, arrs.Max(a => a.Length))
    .Select(x => arrs.Where(a=>a.Length>x).Select(a=>a[x]))
    .SelectMany(x=>x)
    .ToArray();

UPD。

另一种解决方法 - 我刚刚重构@Sinatr回答。

static IEnumerable<T> XYZ<T>(IEnumerable<IList<T>> lists)
{
    if (lists == null)  
        throw new ArgumentNullException();
    var index = 0;

    while (lists.Any(l => l.Count > index))
    {
        foreach (var list in lists)     
            if (list.Count > index)         
                yield return list[index];       
        index++;
    }
}

答案 5 :(得分:0)

我选择:

static void Main(string[] args)
{
    var a = new List<int>() { 1, 2, 3, 4, 5 };
    var b = new List<int>() { 6, 7, 8 };
    var c = new List<int>() { 9, 10, 11, 12 };

    var abc = XYZ<int>(new[] { a, b, c }).ToList();
}

static IEnumerable<T> XYZ<T>(IEnumerable<IList<T>> lists)
{
    if (lists == null)
        throw new ArgumentNullException();
    var finished = false;
    for (int index = 0; !finished; index++)
    {
        finished = true;
        foreach (var list in lists)
            if (list.Count > index) // list != null (prior checking for count)
            {
                finished = false;
                yield return list[index];
            }
    }
}

我必须使用IList使用索引器和Count。它没有创建任何东西(没有枚举器,没有列表等),纯粹是yield return

答案 6 :(得分:0)

对于您的问题,我创建了静态方法,可以根据需要合并任何集合:

public static class CollectionsHandling
{
    /// <summary>
    /// Merge collections to one by index
    /// </summary>
    /// <typeparam name="T">Type of collection elements</typeparam>
    /// <param name="collections">Merging Collections</param>
    /// <returns>New collection {firsts items, second items...}</returns>
    public static IEnumerable<T> Merge<T>(params IEnumerable<T>[] collections)
    {
        // Max length of sent collections
        var maxLength = 0;

        // Enumerators of all collections
        var enumerators = new List<IEnumerator<T>>();

        foreach (var item in collections)
        {
            maxLength = Math.Max(item.Count(), maxLength);
            if(collections.Any())
                enumerators.Add(item.GetEnumerator());
        }
        // Set enumerators to first item
        enumerators.ForEach(e => e.MoveNext());

        var result = new List<T>();
        for (int i = 0; i < maxLength; i++)
        {
            // Add elements to result collection
            enumerators.ForEach(e => result.Add(e.Current));

            // Remobve enumerators, in which no longer have elements
            enumerators = enumerators.Where(e => e.MoveNext()).ToList();
        }

        return result;
    }
}

使用示例:

static void Main(string[] args)
{
    var a = new List<int> { 1, 2, 3, 4, 5 };
    var b = new List<int> { 6, 7, 8 };
    var c = new List<int> { 9, 10, 11, 12 };

    var result= CollectionsHandling.Merge(a, b, c);
}

当您了解它的工作原理时,可以减少较小的方法。