tags:

views:

474

answers:

5

LINQ Groupby query creates a new group for each unique key. I would like to combine multiple groups into a single group based on the key value.

e.g.

var customersData = new[] 
{
    new { id = 1, company = "ABC" },
    new { id = 2, company = "AAA" },
    new { id = 3, company = "ABCD" },
    new { id = 4, company = "XYZ" },
    new { id = 5, company = "X.Y.Z." },
    new { id = 6, company = "QQQ" },
};

var groups = from d in customersData 
             group d by d.company;

Let's say I want ABC, AAA, and ABCD in the same group, and XYZ, X.Y.Z. in the same group.

Is there anyway to achieve this through LINQ queries?

+4  A: 

I am assuming the following:

  1. You meant to have quotes surrounding the company "names" (as below).
  2. Your problem is simply solved by removing the '.'s from each company name.

If these assumptions are correct, the solution is simply the following:

var customersData = new[] {
    new { id = 1, company = "ABC" },
    new { id = 2, company = "A.B.C." },
    new { id = 3, company = "A.B.C." },
    new { id = 4, company = "XYZ" },
    new { id = 5, company = "X.Y.Z." },
    new { id = 6, company = "QQQ" },
};

var groups = from d in customersData
             group d by d.company.Replace(".", "");

If these assumptions are not correct, please clarify and I can help work closer to a solution.

Jason
sorry should have been more clear, the similar groups are not always just the same letters, e.g. I could want to group 'ABC', 'A.B.C.' and 'QQQ' together.
codechobo
A: 
var groups = from d in customersData 
             group d by d.company.Replace(".", "");
Thomas Levesque
+6  A: 

You will need to use the overload of GroupBy that takes an IEqualityComparer.

var groups = customersData.GroupBy(k => k.company, new KeyComparer());

where KeyComparer could look like


public class KeyComparer : IEqualityComparer
{
    public bool Equals(string x, string y)
    {
        // put your comparison logic here 
    }

    public int GetHashCode(string obj)
    {
        // same comparison logic here
    }
}

You can comparer the strings any way you like in the Equals method of KeyComparer.

EDIT:

You also need to make sure that the implementation of GetHashCode obeys the same rules as the Equals method. For example if you just removed the "." and replaced with "" as in other answers you need to do it in both methods like this


public class KeyComparer : IEqualityComparer
{
    public bool Equals(string x, string y)
    {
        return x.Replace(".", "") == y.Replace(".", "");
    }

    public int GetHashCode(string obj)
    {
        return obj.Replace(".", "").GetHashCode();
    }
}
Mike Two
hmm this seems like the way to do it
codechobo
So in the KeyComparer, I would have define a dictionary as the 'lookup table', with the different company names as the Key and they would have the same Value if they belong in the same group. Is there a more optimal way to do this?
codechobo
@Mike Two: Did you implement `GetHashCode` as `return obj.Replace(".", "").GetHashCode()`? That will be a problem if you did not.
Jason
@codechobo Somewhere there is a piece of business logic that relates these, but I have no idea what it is. That is somewhere in your business domain. If there are a set of rules that relate them then you might have something to go on, but without that you have to hard code the relationships or store them in a database.
Mike Two
@Jason, I did not at first, but caught the issue. I've updated the answer. Thanks for catching that.
Mike Two
the database is beyond my realm of control so unfortunately I have to hardcode to get around it. Thanks for the help!
codechobo
A: 
public void int GetId(Company c)
{
  int result = //whatever you want
  return result;
}

then later:

var groups = from d in customersData
             group d by GetId(d.company);
David B
A: 

I think this is what you want:

        var customersData = new[]  
        { 
            new { id = 1, company = "ABC" }, 
            new { id = 2, company = "AAA" }, 
            new { id = 3, company = "ABCD" }, 
            new { id = 4, company = "XYZ" }, 
            new { id = 5, company = "X.Y.Z." }, 
            new { id = 6, company = "QQQ" }, 
        };

        var groups = from d in customersData
                     group d by d.company[0];

        foreach (var group in groups)
        {
            Console.WriteLine("Group " + group.Key);
            foreach (var item in group)
            {
                Console.WriteLine("Item " + item.company);
            }
        }
        Console.ReadLine();
Jonno