How to “zip” or “rotate” a variable number of lists?

后端 未结 8 1610
广开言路
广开言路 2020-11-27 20:47

If I have a list containing an arbitrary number of lists, like so:

var myList = new List>()
{
    new List() { \"a\",          


        
相关标签:
8条回答
  • 2020-11-27 20:48
    (from count in Range(myList[0].Count)
    select new List<string>(
        from count2 in Range(myList.Count)
        select myList[count2][count])
        ).ToList();
    

    It ain't pretty, but I think it'll work.

    0 讨论(0)
  • 2020-11-27 20:56

    You can condense for loops using Range:

    var result = Enumerable.Range(0, myList.Min(l => l.Count))
        .Select(i => myList.Select(l => l[i]).ToList()).ToList();
    
    0 讨论(0)
  • 2020-11-27 21:00

    How about using SelectMany and GroupBy with some indexes?

    // 1. Project inner lists to a single list (SelectMany)
    // 2. Use "GroupBy" to aggregate the item's based on order in the lists
    // 3. Strip away any ordering key in the final answer
    var query = myList.SelectMany(
        xl => xl.Select((vv,ii) => new { Idx = ii, Value = vv }))
           .GroupBy(xx => xx.Idx)
           .OrderBy(gg => gg.Key)
           .Select(gg => gg.Select(xx => xx.Value));
    

    From LinqPad:

    we groupa da items

    0 讨论(0)
  • 2020-11-27 21:04

    Here is a recursive ZipMany implementation inspired from an answer of @Enigmativity to a different question.

    public static IEnumerable<IEnumerable<T>> ZipMany<T>(params IEnumerable<T>[] sources)
    {
        return ZipRecursive(sources);
        IEnumerable<IEnumerable<T>> ZipRecursive(IEnumerable<IEnumerable<T>> ss)
        {
            if (!ss.Skip(1).Any())
            {
                return ss.First().Select(i => Enumerable.Repeat(i, 1));
            }
            else
            {
                return ZipRecursive(ss.Skip(1).ToArray()).Zip(ss.First(), (x, y) => x.Prepend(y));
            }
        }
    }
    

    Advantage: avoids problems related to disposition of enumerators.
    Disadvantage: Scales badly. The recursion overhead becomes noticeable at around 3000 enumerables, and grows exponentially afterwards.

    0 讨论(0)
  • 2020-11-27 21:07

    Take a look at the linqlib project on codeplex, it has a rotate function that does exactly what you need.

    0 讨论(0)
  • 2020-11-27 21:10

    You can roll your own ZipMany instance which manually iterates each of the enumerations. This will likely perform better on larger sequences than those using GroupBy after projecting each sequence:

    public static IEnumerable<TResult> ZipMany<TSource, TResult>(
        IEnumerable<IEnumerable<TSource>> source,
        Func<IEnumerable<TSource>, TResult> selector)
    {
       // ToList is necessary to avoid deferred execution
       var enumerators = source.Select(seq => seq.GetEnumerator()).ToList();
       try
       {
         while (true)
         {
           foreach (var e in enumerators)
           {
               bool b = e.MoveNext();
               if (!b) yield break;
           }
           // Again, ToList (or ToArray) is necessary to avoid deferred execution
           yield return selector(enumerators.Select(e => e.Current).ToList());
         }
       }
       finally
       {
           foreach (var e in enumerators) 
             e.Dispose();
       }
    }
    
    0 讨论(0)
提交回复
热议问题