Bhaskar
Bhaskar

Reputation: 1690

LINQ : Checking against each other item in a list

I have class like below

public class Item
{   
    public long Id {get;set;}
    public long GroupingId {get;set;}
    public long Weight {get;set;}
    public long Tolerance {get;set;}
}

Now I have list of Items with different grouping id. Lets say

List<Item> items  = GetItems();

Now I need to group based groupings id, and make check for each item in that group against each other. How would I do that in LINQ efficiently. Any help much appreciated.

IDictionary<long, long[]> matches = new Dictionary<long, long[]>();

foreach(groupedItems in items.GroupBy(p=>p.GroupingId))
{    
  foreach(item in groupItems)
  {
    // Check item with other items in group items 
    // and if condition is correct take those 2 items.
    // lets say the condition is 
    // (item.Weighting - other.Weighting) > item.Tolerance
    // duplicates could be removed 
    // lets condition for 1,2 is done means no need to do 2 against 1

    var currentItem = item;    
    var matchedOnes = 
         groupItems.Where(p => (Math.Abs(p.Weighting - currentItem.Weighting) > currentItem .Tolerance) && p.Id != currentItem.Id)
                   .ToList();

    if (!matchedOnes.Any())
        continue;

    matches.Add(currentItem.Id, matchedOnes .Select(p=>p.Id).ToArray());
  }
}

I did like above, but its giving duplicates(1,2 and 2,1 are duplicates).. How would I remove the duplicate checks

Upvotes: 0

Views: 1848

Answers (3)

vc 74
vc 74

Reputation: 38179

Try this

var pairs = Enumerable.Range(0, items.Count()).SelectMany(index => 
  items.Skip(index + 1).Select(right => new { left = items.elementAt(index), right }));

var matches = pairs.Where(item => 
               (item.left.Weight - item.right.Weight) > item.left.Tolerance);

The first part creates all the pairs that have to be compared like (1, 2), (1, 3), (2, 3) for a collection of 3 items. The second part selects the pairs that match your condition.

I've also removed the grouping code which you already figured out (items = groupItems).

Upvotes: -2

Rawling
Rawling

Reputation: 50114

As a simple change, try exchanging p.Id != answer.Id for p.Id > answer.Id in your groupItems.Where(...) line.

Upvotes: 3

Serge
Serge

Reputation: 6692

Do you mean this:

        var items = new List<Tuple<int, int>>()
        {
            new Tuple<int, int>(1, 1)
            , new Tuple<int, int>(1, 2)
            , new Tuple<int, int>(2, 2)
            , new Tuple<int, int>(2, 2)
            , new Tuple<int, int>(2, 3)
            , new Tuple<int, int>(3, 2)
            , new Tuple<int, int>(3, 3)
            , new Tuple<int, int>(4, 4)
            , new Tuple<int, int>(4, 3)
            , new Tuple<int, int>(4, 4)
        }.Select(kp => new { id = kp.Item1, data = kp.Item2 });

        var res = (
            from i1 in items
            from i2 in items
            where i1.id < i2.id
                /* Custom Filter goes there */
                && i1.data == i2.data
            select new { i1 = i1, i2 = i2 }
        );

Upvotes: 0

Related Questions