考虑一组6个[StringKey,Value]数组,伪代码:
object[,2][6] KeyValueArrays; //illustr as array, could also be List<> or Dict<>
我想将其转换为单个表格:
object[,7] KeyWithUpTo6Values //outer join of the 6 orig key-value sets
如果给定的键出现在6个原始[Key,Value]数组中的(例如)3中,那么该键的连接行将包含3个值和3个空值。
问题:我是否可以使用简单的容器(如数组和通用列表&amp;)来使用LINQ。字典?
答案 0 :(得分:2)
我想我可能会遗漏一些东西,但事实上你的问题提到了通用列表&amp;字典,我认为当你说数组时,你指的是矢量数据结构的一些王者。
因此,可以使用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,但除非您使用其他类型的数据结构(即List)表示您的行,您将无法动态执行此操作,即对于任何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的值。如果恰好是一个充分的解决方案,它实际上很简单,尽管例子我写道可能有点过于复杂。回到上面的查询,您将看到来自/ join /来自DefaultIfEmpty 的的重复模式。这个模式是从here复制的,它实际上很简单:它通过一些键将两个结构连接到另一个结果结构(上面的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}
}
}
}
通过添加第二个where
子句,在LINQ代码中实现else块,即使没有匹配也要求行,这是一个空列表,可以在调用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();
}
}
}
答案 1 :(得分:0)
多维数组不实现IEnumerable<T>
,因此您将无法使用LINQ。另一方面,锯齿状数组可以由LINQ操纵。