对象数组上的Linq外部联接
本文关键字:外部 Linq 数组 对象 | 更新日期: 2023-09-27 18:28:48
考虑一组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个null。
问题:我可以用LINQ只使用简单的容器(如数组和泛型列表)来完成这项工作吗;字典?
我想我可能遗漏了一些东西,但事实上,你的问题提到了泛型列表&字典,我认为当你说数组时,你指的是某种向量数据结构之王。
因此,可以使用Dictionary<T1,T2>
来存储键值对。例如,假设您的键是一个string
和一个具有integer类型单个属性的值类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/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}
}
}
}
之前在LINQ代码中通过添加第二个where
子句实现了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();
}
}
}
多维数组不实现IEnumerable<T>
,因此您将无法使用LINQ。另一方面,锯齿阵列可以由LINQ操作。