Linq: Преобразование плоской структуры к иерархическому

Что является самым легким и несколько эффективным способом преобразовать плоскую структуру:

object[][] rawData = new object[][] 
{ 
  { "A1", "B1", "C1" }, 
  { "A1", "B1", "C2" },
  { "A2", "B2", "C3" }, 
  { "A2", "B2", "C4" }
  // .. more 
};

в иерархическую структуру:

class X
{
  public X () 
  {
    Cs = new List<string>();
  }
  public string A { get; set; }
  public string B { get; set; }
  public List<string> Cs { get; private set; }
}

результат должен быть похожим на это

// pseudo code which describes structure:
result =
{
  new X() { A = "A1", B = "B1", Cs = { "C1", "C2" } },
  new X() { A = "A2", B = "B2", Cs = { "C3", "C4" } }
}

Предпочтительно с помощью методов расширения Linq. Целевой класс X мог быть изменен (например, общедоступный метод set для Списка), только если не возможный / полезный, как это теперь.

13
задан Stefan Steinegger 18 June 2010 в 13:51
поделиться

4 ответа

для этого конкретного случая:

   .GroupBy( x => new { a = x[0], b = x[1] } )
   .Select( x => new { A = x.Key.a, B = x.Key.b, C = x.Select( c => c[2] ) })
7
ответ дан 2 December 2019 в 01:20
поделиться

Что-то вроде этого должно работать, если глубина вашей иерархии ограничена (как в вашем примере, где у вас есть только три уровня A, B и C). Я немного упростил ваш X :

class X {
    public string A { get; set; }
    public string B { get; set; }
    public List<string> Cs { get; set; }
} 

Затем вы можете использовать вложенный GroupBy столько раз, сколько вам нужно (в зависимости от глубины иерархии). Было бы также относительно легко переписать это в рекурсивный метод (который работал бы для произвольно глубоких иерархий):

// Group by 'A'
rawData.GroupBy(aels => aels[0]).Select(a => 
  // Group by 'B'
  a.GroupBy(bels => bels[1]).Select(b =>
    // Generate result of type 'X' for the current grouping
    new X { A = a.Key, B = b.Key, 
            // Take the third element 
            Cs = b.Select(c => c[2]).ToList() }));

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

2
ответ дан 2 December 2019 в 01:20
поделиться

Если члены X являются строками, а Cs - частным набором, а rawData - массивом массивов объектов, я бы добавил конструктор в X public X (строка a, строка b, List cs) , а затем выполнить этот код

var query = from row in rawData
            group row by new { A = row[0], B = row[1] } into rowgroup
            select new X((string)rowgroup.Key.A, (string)rowgroup.Key.B, rowgroup.Select(r => (string)r[2]).ToList());

Это относится к следующим необработанным данным

object[][] rawData = new object[][]  
    {  
        new object[] { "A1", "B1", "C1" },  
        new object[] { "A1", "B1", "C2" }, 
        new object[] { "A2", "B2", "C3" },  
        new object[] { "A2", "B2", "C4" } 
        // .. more  
    };
1
ответ дан 2 December 2019 в 01:20
поделиться

Я хотел посмотреть, смогу ли я написать это без анонимных экземпляров. Это не так уж плохо:

IEnumerable<X> myList =
  from raw0 in rawData
  group raw0 by raw0[0] into g0
  let g1s =
  (
    from raw1 in g0
    group raw1 by raw1[1]
  )
  from g1 in g1s
  select new X()
  {
    A = g0.Key,
    B = g1.Key,
    C = g1.Select(raw2 => raw2[2]).ToList()
  }
0
ответ дан 2 December 2019 в 01:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: