LINQ C # - объединение нескольких групп

Запрос LINQ Groupby создает новую группу для каждого уникального ключа. Я хотел бы объединить несколько групп в одну группу на основе значения ключа.

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;

Скажем, я хочу, чтобы ABC, AAA и ABCD были в одной группе, а XYZ, X.Y.Z. в той же группе.

Есть ли способ добиться этого с помощью запросов LINQ?


person codechobo    schedule 08.09.2009    source источник
comment
Вы сказали на основе значения ключа, но не указали, как следует использовать значение ключа. Что общего?   -  person Jon Skeet    schedule 08.09.2009
comment
вам нужно по чему их сгруппировать. Компилятор не может угадать, как вы хотите сгруппировать, поэтому все элементы в одной группе должны иметь хотя бы одно общее значение.   -  person Rune FS    schedule 08.09.2009
comment
нет общей общности, "группа групп" определенно должна быть где-то явно определена   -  person codechobo    schedule 08.09.2009


Ответы (4)


Вам нужно будет использовать перегрузку GroupBy, которая принимает IEqualityComparer.

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

где KeyComparer может выглядеть как


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
    }
}

Вы можете сравнивать строки любым способом в методе Equals KeyComparer.

РЕДАКТИРОВАТЬ:

Также необходимо убедиться, что реализация GetHashCode подчиняется тем же правилам, что и метод Equals. Например, если вы только что удалили символ "." и замените на "", как в других ответах, вам нужно сделать это в обоих методах, подобных этому


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();
    }
}
person Mike Two    schedule 08.09.2009
comment
хм, похоже, это способ сделать это - person codechobo; 08.09.2009
comment
Итак, в KeyComparer я бы определил словарь как «таблицу поиска» с разными названиями компаний в качестве ключа, и у них было бы одинаковое значение, если бы они принадлежали к одной группе. Есть ли более оптимальный способ сделать это? - person codechobo; 08.09.2009
comment
@Mike Two: Вы реализовали GetHashCode как return obj.Replace(".", "").GetHashCode()? Если вы этого не сделали, это будет проблемой. - person jason; 08.09.2009
comment
@codechobo Где-то есть часть бизнес-логики, которая связывает их, но я понятия не имею, что это такое. Это где-то в сфере вашего бизнеса. Если существует набор правил, которые их связывают, тогда вам может быть чем заняться, но без этого вам придется жестко закодировать отношения или сохранить их в базе данных. - person Mike Two; 08.09.2009
comment
@ Джейсон, я сначала не понял, но уловил проблему. Я обновил ответ. Спасибо, что уловили это. - person Mike Two; 08.09.2009
comment
база данных находится вне моего контроля, поэтому, к сожалению, мне нужно жестко запрограммировать ее, чтобы обойти ее. Спасибо за помощь! - person codechobo; 09.09.2009

Я предполагаю следующее:

  1. Вы хотели заключить в кавычки "названия" компаний (как показано ниже).
  2. Ваша проблема решается просто удалением "." Из названия каждой компании.

Если эти предположения верны, решение просто следующее:

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(".", "");

Если эти предположения неверны, поясните, пожалуйста, и я помогу найти решение.

person jason    schedule 08.09.2009
comment
извините, должно было быть более ясным, похожие группы не всегда являются одними и теми же буквами, например Я мог бы захотеть сгруппировать «ABC», «A.B.C.» и QQQ вместе. - person codechobo; 08.09.2009

public void int GetId(Company c)
{
  int result = //whatever you want
  return result;
}

тогда позже:

var groups = from d in customersData
             group d by GetId(d.company);
person Amy B    schedule 09.09.2009

Думаю, это то, что вам нужно:

        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();
person Jonno    schedule 12.08.2010