LINQ меняет столбцы на строки

Есть ли причудливое выражение LINQ, которое могло бы позволить мне сделать следующее намного проще. У меня есть List<List<double>>, предполагая, что List являются столбцами в 2d-матрице, я хочу поменять список столбцов на список строк. У меня есть следующее очевидное решение:

int columns = 5;
var values; // assume initialised as List<List<double>>()

var listOfRows = new List<List<double>>();
for (int i = 0; i < columns ; i++)
{
    List<double> newRow = new List<double>();
    foreach (List<double> value in values)
    {
        newRow.Add(value[i]);
    }
    listOfRows.Add(newRow);
}

Ответ 1

Вы можете легко LINQify для внутреннего цикла:

vector.AddRange(values.Select(value => value[i]));

Независимо от того, улучшает ли читаемость читаемость до вас полностью!

Ответ 2

Здесь выражение Linq, которое будет делать то, что вы хотите - глядя на него, я лично придерживался вложенных циклов foreach, но гораздо легче читать:

var columnList= new  List<List<double>>();
columnList.Add(new List<double>() { 1, 2, 3 });
columnList.Add(new List<double>() { 4, 5, 6 });
columnList.Add(new List<double>() { 7, 8, 9 });
columnList.Add(new List<double>() { 10, 11, 12 });

int columnCount = columnList[0].Count;
var rowList = columnList.SelectMany(x => x)
                        .Select((x, i) => new { V = x, Index = i })
                        .GroupBy(x => (x.Index + 1) % columnCount)
                        .Select(g => g.Select( x=> x.V).ToList())
                        .ToList();

Этот пример также будет работать только с матрицей с фиксированным числом столбцов. В основном это сглаживание матрицы в список, а затем создание списка строк путем группировки по индексу элемента в списке по модулю счетчика столбцов.

Edit:

Другой подход, гораздо ближе к вложенному циклу и, вероятно, к подобной производительности, помимо накладных расходов.

int columnCount = columnList[0].Count;
int rowCount = columnList.Count;

var rowList =  Enumerable.Range(0, columnCount)
                         .Select( x => Enumerable.Range(0, rowCount)
                                                 .Select(y => columnList[y][x])
                                                 .ToList())
                         .ToList();

Ответ 3

var inverted = Enumerable.Range(0, columnCount)
               .Select(index => columnList.Select(list => list[index]));

Короче говоря, мы перечисляем индекс столбца из диапазона и используем его для сбора n-го элемента каждого списка.

Обратите внимание, что вам нужно проверить, что каждый список имеет одинаковое количество столбцов.

Ответ 4

Я сочетаю некоторые из приведенных выше ответов, в которых иногда были перевернуты столбцы и строки из исходного ответа или из соглашения, к которому я привык: строка ссылается на первый индекс и столбец на внутренний (второй) индекс. например значения [строка] [столбец]

    public static List<List<T>> Transpose<T>(this List<List<T>> values)
    {
        if (values.Count == 0 || values[0].Count == 0)
        {
            return new List<List<T>>();
        }

        int ColumnCount = values[0].Count;

        var listByColumns = new List<List<T>>();
        foreach (int columnIndex in Enumerable.Range(0, ColumnCount))
        {
            List<T> valuesByColumn = values.Select(value => value[columnIndex]).ToList();
            listByColumns.Add(valuesByColumn);
        }
        return listByColumns;
    }            

На самом деле слово row и column - это просто наше мышление о данных в строках и столбцах, и иногда добавляет больше путаницы, чем их решение.

Мы фактически просто заменяем внутренний индекс внешнего индекса. (или переворачивая индексы вокруг). Таким образом, можно также просто определить следующий метод расширения., Снова я заимствовал из вышеперечисленных решений, просто поместил его во что-то, что я считаю читаемым и довольно компактным.

Проверяет, что внутренние списки имеют одинаковый размер.

    public static List<List<T>> InsideOutFlip<T>(this List<List<T>> values)
    {
        if (values.Count == 0 || values[0].Count == 0)
        {
            return new List<List<T>>();
        }

        int innerCount = values[0].Count;

        var flippedList = new List<List<T>>();
        foreach (int innerIndex in Enumerable.Range(0, innerCount))
        {
            List<T> valuesByOneInner = values.Select(value => value[innerIndex]).ToList();
            flippedList.Add(valuesByOneInner);
        }
        return flippedList;
    }            

Ответ 5

Здесь тот, который работает для прямоугольных (не-оборванных) матриц. Здесь код С# работает в LinqPad, свободном интерактивном инструменте С#.

Я определяю постфиксный оператор (т.е. метод расширения) "Transpose". Используйте оператор следующим образом:

    var rand = new Random();

    var xss = new [] {
        new [] {rand.NextDouble(), rand.NextDouble()},
        new [] {rand.NextDouble(), rand.NextDouble()},
        new [] {rand.NextDouble(), rand.NextDouble()},
    };

    xss.Dump("Original");
    xss.Transpose().Dump("Transpose");

приводит к чему-то вроде этого:

Original
0.843094345109116
0.981432441613373

0.649207864724662
0.00594645645746331

0.378864820291691
0.336915332515219


Transpose
0.843094345109116
0.649207864724662
0.378864820291691

0.981432441613373
0.00594645645746331
0.336915332515219

Суть реализации этого оператора следующая

    public static IEnumerable<IEnumerable<T>> Transpose<T>(this IEnumerable<IEnumerable<T>> xss)
    {
        var heads = xss.Heads();
        var tails = xss.Tails();

        var empt = new List<IEnumerable<T>>();
        if (heads.IsEmpty())
            return empt;
        empt.Add(heads);
        return empt.Concat(tails.Transpose());
    }

Вот полная реализация, и некоторые строки закомментировали, что вы можете раскомментировать, как работает эта функция.

void Main()
{
    var rand = new Random();

    var xss = new [] {
        new [] {rand.NextDouble(), rand.NextDouble()},
        new [] {rand.NextDouble(), rand.NextDouble()},
        new [] {rand.NextDouble(), rand.NextDouble()},
    };
    xss.Dump("Original");
    xss.Transpose().Dump("Transpose");
}

public static class Extensions
{
    public static IEnumerable<T> Heads<T>(this IEnumerable<IEnumerable<T>> xss)
    {
        Debug.Assert(xss != null);
        if (xss.Any(xs => xs.IsEmpty()))
            return new List<T>();
        return xss.Select(xs => xs.First());
    }

    public static bool IsEmpty<T>(this IEnumerable<T> xs)
    {
        return xs.Count() == 0;
    }

    public static IEnumerable<IEnumerable<T>> Tails<T>(this IEnumerable<IEnumerable<T>> xss)
    {
        return xss.Select(xs => xs.Skip(1));
    }

    public static IEnumerable<IEnumerable<T>> Transpose<T>(this IEnumerable<IEnumerable<T>> xss)
    {
//      xss.Dump("xss in Transpose");
        var heads = xss.Heads()
//          .Dump("heads in Transpose")
            ;
        var tails = xss.Tails()
//          .Dump("tails in Transpose")
            ;

        var empt = new List<IEnumerable<T>>();
        if (heads.IsEmpty())
            return empt;
        empt.Add(heads);
        return empt.Concat(tails.Transpose())
//          .Dump("empt")
            ;
    }
}