kshahar
kshahar

Reputation: 10513

Join two ordered collections with LINQ

I've got two data types, Foo and Bar, that have a property to determine order:

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

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

Then I've got two collections of these types. I'd like to join the collections, so that the result will be contain pairs of Foo and Bar. The number of pairs should be the number of Bar elements.

Each pair should consist of a Bar element and the most "recent" Foo element (with largest Foo Order value, bounded by the current Bar Order value).

For example, for the following collections (some statements omitted):

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

The result would be:

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

How can I achieve this with LINQ and C# 4.0?

Upvotes: 1

Views: 655

Answers (3)

Ohad Schneider
Ohad Schneider

Reputation: 38106

If you allow foo repetitions in cases where the bound foo is the same for several bar objects:

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

Of course it's still less efficient than iterating, since TakeWhile will be called for each bars object (starting from the beginning each time)

What I mean by foo repetitions is that for an input such as

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") };

The result would be

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

Upvotes: 1

Ani
Ani

Reputation: 113402

Assuming foos are sorted by Order, you can do:

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

Otherwise, I suggest sorting the foos first.

You can make this query much more efficient by using binary search (e.g. wih Array.BinarySearch) instead of linear search as in my sample.

Upvotes: 2

Gaurav Agrawal
Gaurav Agrawal

Reputation: 4431

By using union() of Linq you can join 2 same element ordered collections...

Upvotes: 0

Related Questions