Я думаю, что я что-то упускаю, но тот факт, что в вашем вопросе упоминается общие списки и словари , я думаю, что когда вы говорите массив, вы имеете в виду некий король структуры векторных данных.
Таким образом, пары ключ-значение могут быть сохранены с использованием Dictionary<T1,T2>
. Например, предположим, что вы используете ключ string
и класс значений MyValueClass
с одним свойством целочисленного типа. Ваша декларация данных будет выглядеть так:
class Program
{
class MyValueClass
{
public int Value {get;set;}
}
// Other elements elided for clarity
private Dictionary<string, MyValueClass> data = new Dictionary<string, MyValueClass>();
}
Теперь вы заявили, что у вас будет N таких структур, для которых вы хотите выполнить внешнее соединение. Например,
private Dictionary<string, MyValueClass>[] data = new Dictionary<string, MyValueClass>[6]();
Проблема здесь в том, что количество «столбцов» в типе объединенной структуры зависит от этого N, но если вы не используете какой-либо другой тип структуры данных (например, список) для представления ваша строка , вы не сможете сделать это динамически, т. е. для любого N, потому что данные в C # объявляются статически.
Чтобы проиллюстрировать это, проверьте запрос ниже, где я предполагаю, что массив имеет размерность 4:
var query = from d0 in _data[0]
join d1 in _data[1] on d0.Key equals d1.Key into d1joined
from d1 in d1joined.DefaultIfEmpty()
join d2 in _data[2] on d1.Key equals d2.Key into d2joined
from d2 in d2joined.DefaultIfEmpty()
join d3 in _data[3] on d2.Key equals d3.Key into d3joined
from d3 in d3joined.DefaultIfEmpty()
select new
{
d0.Key,
D0 = d0.Value,
D1 = d1.Value,
D2 = d2.Value,
D3 = d3.Value,
};
Не зацикливайтесь на соединениях, я объясню позже, но проверьте оператор select new
. Обратите внимание: когда Linq собирает этот анонимный тип, он должен знать точное число свойств - наших столбцов - потому что это часть синтаксиса.
Так что если вы хотите, вы можете написать запрос, чтобы выполнить то, что вы просите, но он будет работать только для известного значения N. В случае, если это окажется достаточным решением, на самом деле это просто, хотя пример I написано может быть немного сложнее. Возвращаясь к приведенному выше запросу, вы увидите повторяющийся шаблон из / join / from DefaultIfEmpty . Этот шаблон был скопирован с здесь , и он работает на самом деле просто: он соединяет две структуры одним ключом в другую результирующую структуру (into dnjoined
выше). Linq обработает все записи в левом списке и для каждой из них обработает все записи в правом списке (декартовой план N1 x N2), например:
foreach (var d0 in _data[0])
{
foreach (var d1 in _data[1])
{
if (d0.Key == d1.Key)
{
// Produces an anonymous structure of { d0.Key, d0.Value, d1.Value }
// and returns it.
}
}
}
Итак, операция внутреннее объединение такая же, как объединение каждой строки с последующим выбором тех, где ключи совпадают. Внешнее соединение отличается созданием строки, даже если ключ не совпадает, поэтому в нашем псевдокоде это будет выглядеть примерно так:
foreach (var d0 in _data[0])
{
foreach (var d1 in _data[1])
{
if (d0.Key == d1.Key)
{
// Produces an anonymous structure of { d0.Key, d0.Value, d1.Value }
// and returns it.
}
else
{
// Produce a anonymous structure of {d0.Key, d0.Value, null}
}
}
}
Блок else достигается в коде LINQ раньше, добавляя второе предложение where
, которое запрашивает строки, даже если совпадений нет, - это пустой список, который может возвращать данные при вызове DefaultIfEmpty. (еще раз, смотрите ссылку выше, чтобы получить больше информации)
Ниже я напишу полный пример, который использует структуру данных и запрос linq, о котором я упоминал выше. Надеюсь, это говорит само за себя:
using System;
using System.Collections.Generic;
using System.Linq;
namespace TestZone
{
class Example
{
#region Types
class MyValue
{
public int Value { get; set; }
public override string ToString()
{
return string.Format("MyValue(Value = {0})", Value);
}
}
#endregion // Types
#region Constants
/// <summary>
/// Our N
/// </summary>
private const int NumberOfArrays = 4;
/// <summary>
/// How many rows per dictionary
/// </summary>
private const int NumberOfRows = 10;
#endregion // Constants
#region Fields
private Dictionary<string, MyValue>[] _data = new Dictionary<string, MyValue>[NumberOfArrays];
#endregion // Fields
#region Constructor
public Example()
{
for (var index = 0; index < _data.Length; index++)
{
_data[index] = new Dictionary<string, MyValue>(NumberOfRows);
}
}
#endregion // Constructor
public void GenerateRandomData()
{
var rand = new Random(DateTime.Now.Millisecond);
foreach (var dict in _data)
{
// Add a number of rows
for (var i = 0; i < NumberOfRows; i++)
{
var integer = rand.Next(10); // We use a value of 10 so we have many collisions.
dict["ValueOf" + integer] = new MyValue { Value = integer };
}
}
}
public void OuterJoin()
{
// To get the outer join, we have to know the expected N before hand, as this example will show.
// Do multiple joins
var query = from d0 in _data[0]
join d1 in _data[1] on d0.Key equals d1.Key into d1joined
from d1 in d1joined.DefaultIfEmpty()
join d2 in _data[2] on d1.Key equals d2.Key into d2joined
from d2 in d2joined.DefaultIfEmpty()
join d3 in _data[3] on d2.Key equals d3.Key into d3joined
from d3 in d3joined.DefaultIfEmpty()
select new
{
d0.Key,
D0 = d0.Value,
D1 = d1.Value,
D2 = d2.Value,
D3 = d3.Value,
};
foreach (var q in query)
{
Console.WriteLine(q);
}
}
}
class Program
{
public static void Main()
{
var m = new Example();
m.GenerateRandomData();
m.OuterJoin();
}
}
}