使用LINQ加入两个有序集合

时间:2011-11-23 13:16:34

标签: linq join c#-4.0 collections

我有两种数据类型FooBar,它们具有确定顺序的属性:

class Foo
{
    public int Order { get; set; }
    public string FooValue { get; set; }
}

class Bar
{
    public int Order { get; set; }
    public string BarValue { get; set; }
}

然后我有两个这些类型的集合。我想加入这些集合,因此结果将包含FooBar对。对的数量应该是Bar个元素的数量。

每对应包含Bar元素和最“最近”的Foo元素(具有最大的Foo Order值,以当前条形Order值为界)

例如,对于以下集合(省略了一些语句):

var foos = new [] { (1, "Foo1"), (2, "Foo2"), (5, "Foo5"), (7, "Foo7") };
var bars = new [] { (1, "Bar1"), (6, "Bar6") };

结果将是:

result = { 
    ((1, "Bar1"), (1, "Foo1")),
    ((6, "Bar6"), (5, "Foo5"))
};

如何使用LINQ和C#4.0实现这一目标?

3 个答案:

答案 0 :(得分:2)

假设foosOrder排序,您可以执行以下操作:

var fubars = from bar in bars
             let bestFoo = foos.TakeWhile(foo => foo.Order <= bar.Order)
                               .LastOrDefault()
             select new { Bar = bar, Foo = bestFoo };

否则,我建议先排序foos

您可以使用二进制搜索(例如wih Array.BinarySearch)而不是我的示例中的线性搜索来提高此查询的效率。

答案 1 :(得分:1)

如果在多个foo对象的绑定foo相同的情况下允许bar次重复:

var result = bars.Zip(foos, 
        (b,f) => Tuple.Create(b, foos.TakeWhile(foo => foo.Order <= b.Order).Last()));

当然它的效率仍然低于迭代,因为TakeWhile将为每个bars对象调用(每次从头开始)

foo重复的意思是对于

这样的输入
var foos = new [] { new Foo(1, "Foo1"), new Foo(3, "Foo3"), new Foo(5, "Foo5")};
var bars = new [] { new Bar(1, "Bar1"), new Bar(2, "Bar2") };

结果将是

{ 
    ((1, "Bar1"), (1, "Foo1")),
    ((2, "Bar2"), (1, "Foo1")) //Foo1 again
};

答案 2 :(得分:0)

通过使用Linq的union(),您可以加入2个相同的元素有序集合...