Rotate - Transposing a List> using LINQ C#

前端 未结 6 1057
半阙折子戏
半阙折子戏 2020-11-29 11:12

I\'m having a List>, which is return from the remote data source (i.e., WCF). So, I need to modify the following data into a user-frien

相关标签:
6条回答
  • You can use Enumerable.Range and Enumerable.ElementAtOrDefault:

    List<List<string>> rotated = Enumerable.Range(0, PersonInfo.Max(list => list.Count))
     .Select(i => PersonInfo.Select(list => list.ElementAtOrDefault(i)).ToList())
     .ToList();
    

    PersonInfo.Max(list => list.Count) returns the max-size of the lists. This will be the new size of the main list, in this case 3. Enumerable.Range is like a for-loop. For every list it will now select all strings at these indexes. If the sizes are different you'll get null(because of ElementAtOrDefault).

    If the lists had the same size you can apply the same query to get the original list back:

    PersonInfo = Enumerable.Range(0, rotated.Max(list => list.Count))
     .Select(i => rotated.Select(list => list.ElementAtOrDefault(i)).ToList())
     .ToList();
    

    As extension:

    public static IEnumerable<IList<T>> Rotate<T>(this IEnumerable<IList<T>> sequences)
    {
        var list = sequences as IList<IList<T>> ?? sequences.ToList();
        int maxCount = list.Max(l => l.Count);
        return Enumerable.Range(0, maxCount)
            .Select(i => list.Select(l => l.ElementAtOrDefault(i)).ToList());
    }
    

    Usage:

    IEnumerable<IList<string>> rotated = PersonInfo.Rotate();
    IEnumerable<IList<string>> rotatedPersonInfo = rotated.Rotate(); // append ToList to get the original list
    
    0 讨论(0)
  • 2020-11-29 11:58

    This is a simple and flexible solution, it will handle multiple inner lists with any number of dimensions.

    List<List<string>> PersonInfo = new List<List<string>>()
    {
        new List<string>() {"John", "Peter", "Watson"},
        new List<string>() {"1000", "1001", "1002"}
    };
    
    
    var result = PersonInfo
        .SelectMany(inner => inner.Select((item, index) => new { item, index }))
        .GroupBy(i => i.index, i => i.item)
        .Select(g => g.ToList())
        .ToList();
    
    0 讨论(0)
  • 2020-11-29 11:58

    Assuming there are only ever 2 lists inside PersonInfo:

    var rotated = PersonInfo[0]
        .Zip(PersonInfo[1], (a, b) => new List<string> { a, b }).ToList();
    

    If there can be any number of Lists inside of PersonInfo:

    Enumerable.Range(0, PersonInfo[0].Count)
        .Select(i => PersonInfo.Select(lst => lst[i]).ToList()).ToList();
    
    0 讨论(0)
  • 2020-11-29 12:02

    Here is a extension method

    public static IEnumerable<IEnumerable<T>> Pivot<T>(this IEnumerable<IEnumerable<T>> source)
    {
        var enumerators = source.Select(e => e.GetEnumerator()).ToArray();
        try
        {
            while (enumerators.All(e => e.MoveNext()))
            {
                yield return enumerators.Select(e => e.Current).ToArray();
            }
        }
        finally
        {
            Array.ForEach(enumerators, e => e.Dispose());
        }
    }
    

    so you can

    var result = PersonInfo.Pivot();
    
    0 讨论(0)
  • 2020-11-29 12:04

    This extends the Zip idea above to any number of lists. Zip will truncate the row lists to the smallest rank.

    List<List<string>> PersonInfo = new List<List<string>>()
    {
        new List<string>() {"John", "Peter", "Watson"},
        new List<string>() {"1000", "1001", "1002"},
        new List<string>() {"2000", "2001", "2002"},
        new List<string>() {"3000", "3001", "3002"}
    };
    
    var seed = Enumerable.Empty<List<string>>();
    var transformed = PersonInfo.Aggregate(seed, (acc, r) =>
       acc.Any()
     ? acc.Zip(r, (row, nextElement) => { row.Add(nextElement); return row; })
     : r.Select(e => new List<string> { e }) //initialize target list using first row
    ); 
    
    0 讨论(0)
  • 2020-11-29 12:14

    Try this:

    List<List<string>> PersonInfo = new List<List<string>>(){
    new List<string>() {"John", "Peter", "Watson"},
    new List<string>() {"1000", "1001", "1002"}};
    
    List<List<string>> PivitedPersonInfo = new List<List<string>>();
    for (int i = 0; i < PersonInfo.First().Count; i++)
    {
        PivitedPersonInfo.Add(PersonInfo.Select(x => x.ElementAt(i)).ToList());
    }
    
    0 讨论(0)
提交回复
热议问题