Использование некоторых свойств пользовательского класса в качестве уникального ключа к другому свойству (которое создается в виде значений) в том же классе с использованием словаря в C# - PullRequest
2 голосов
/ 23 апреля 2020

У меня есть тип класса скажем ValueSetting, как показано ниже, и я пытаюсь перебрать List так, чтобы для всех общих записей param1, param2, param3 в списке мне нужно было выполнить операцию c для каждого из param4. позволь мне объяснить . в таблице ниже записи с метками красной стрелки (param1, param2, param3) должны быть чем-то вроде ключа для списка из param4 (500, 600). Таким образом, объединяя их

10, Foo, AB c => (500, 600)

20, Boo, AB c => (500, 600)

30, foo, ab c => (500)

Итак, это то, что я могу поместить в словарь с помощью param1, param2, param3 в качестве ключа для List <значения param4>? Как это сделать? или есть лучший способ хранить их вместо словаря?

enter image description here

public class ValueSetting 
{  
  public int param1;
  public string param2;
  public string param3;
  public int param4;
}

Ответы [ 2 ]

2 голосов
/ 23 апреля 2020

Для структурирования данных можно использовать способ, которым вы задаете LINQ ToLookup method. Из MSDN:

Метод ToLookup возвращает Lookup, словарь «один ко многим», который сопоставляет ключи с коллекциями значений. Lookup отличается от Dictionary, который выполняет однозначное сопоставление ключей с отдельными значениями.

Чтобы создать Lookup с использованием метода ToLookup, мы должны определить:

  • key класс с переопределенными методами GetHashCode и Equals; этот класс key будет определять способ группировки входных данных;
  • keySelector, который будет использоваться для создания ключей из входных данных;
  • elementSelector, который будет использоваться для выбора значений для соответствующие ключи.

Первый подход: использование анонимного типа в качестве ключа.

Мы можем использовать анонимный тип в качестве ключ для создания Lookup. Anonymous types автоматически реализует GetHashCode и Equals. Из MSDN:

Поскольку методы Equals и GetHashCode для анонимных типов определены в терминах методов Equals и GetHashCode свойств, два экземпляра одного и того же анонимного типа равны, только если все их свойства равно.

Вот как мы можем создать Lookup, используя anonymous type в качестве ключа:

var list = new List<ValueSetting>
{
    new ValueSetting {param1 = 10, param2 = "foo", param3 = "abc", param4 = 500},
    new ValueSetting {param1 = 20, param2 = "boo", param3 = "abc", param4 = 500},
    new ValueSetting {param1 = 10, param2 = "foo", param3 = "abc", param4 = 600},
    new ValueSetting {param1 = 20, param2 = "boo", param3 = "abc", param4 = 600},
    new ValueSetting {param1 = 30, param2 = "foo", param3 = "abc", param4 = 500},
};

var lookup = list.ToLookup(
    vs => new {vs.param1, vs.param2, vs.param3}, // Group key.
    vs => vs.param4);                            // Group values.

foreach (var item in lookup)
{
    Console.WriteLine("{0} => ({1})", item.Key, string.Join(", ", item.Select(i => i)));
}

Этот подход удобен, если вы собираетесь использовать Lookup в одном методе, поскольку объекты анонимного типа не могут быть возвращены из метода и переданы другому методу в качестве аргумента.


Второй подход: введение класса ключей.

Другой подход заключается в определении класса, который будет использоваться в качестве ключа группирования. В этом классе мы должны переопределить методы GetHashCode и Equals. Если вы используете Visual Studio 2015 или более позднюю версию, вы можете автоматически сгенерировать эти элементы . Вот как мы можем определить такой класс:

public class Key
{
    public int param1;
    public string param2;
    public string param3;

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != GetType()) return false;
        Key other = (Key) obj;
        Key other = (Key) obj;
        return param1 == other.param1 && 
               string.Equals(param2, other.param2) && 
               string.Equals(param3, other.param3);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            var hashCode = param1;
            hashCode = (hashCode * 397) ^ (param2 != null ? param2.GetHashCode() : 0);
            hashCode = (hashCode * 397) ^ (param3 != null ? param3.GetHashCode() : 0);
            return hashCode;
        }
    }
}

И затем, используя этот ключевой класс, мы можем создать Lookup:

var list = new List<ValueSetting>
{
    new ValueSetting {param1 = 10, param2 = "foo", param3 = "abc", param4 = 500},
    new ValueSetting {param1 = 20, param2 = "boo", param3 = "abc", param4 = 500},
    new ValueSetting {param1 = 10, param2 = "foo", param3 = "abc", param4 = 600},
    new ValueSetting {param1 = 20, param2 = "boo", param3 = "abc", param4 = 600},
    new ValueSetting {param1 = 30, param2 = "foo", param3 = "abc", param4 = 500},
};

ILookup<Key, int> lookup = list.ToLookup(
    vs => new Key {param1 = vs.param1, param2 = vs.param2, param3 = vs.param3}, // Group key.
    vs => vs.param4); // Group values.

foreach (IGrouping<Key, int> item in lookup)
{
    Console.WriteLine("{0}, {1}, {2} => ({3})",
        item.Key.param1, item.Key.param2, item.Key.param3,
        string.Join(", ", item.Select(i => i)));
}

Этот подход можно использовать, если вы хотите вернуть Lookup из метода, или передать его в качестве аргумента другому методу, или сохранить как свойство класса (и в других случаях при создании Lookup необходимо использовать вне метода, в котором он был создан) .

1 голос
/ 23 апреля 2020

Похоже, что вы можете просто использовать System.Linq.Enumerable.GroupBy, чтобы сделать это:

var list = new List<ValueSetting>
{
    new ValueSetting {param1 = 10, param2 = "foo", param3 = "abc", param4 = 500},
    new ValueSetting {param1 = 20, param2 = "boo", param3 = "abc", param4 = 500},
    new ValueSetting {param1 = 10, param2 = "foo", param3 = "abc", param4 = 600},
    new ValueSetting {param1 = 20, param2 = "boo", param3 = "abc", param4 = 600},
    new ValueSetting {param1 = 30, param2 = "foo", param3 = "abc", param4 = 500},
};

var grouped = list.GroupBy(x => new { x.param1, x.param2, x.param3 });

foreach (var grp in grouped)
{
    Console.WriteLine($"{grp.Key} => ({string.Join(", ", grp.Select(x => x.param4))})");
}

Объяснение:

  • Группировать по трем полям param1, param2 и param3 с использованием анонимного класса
  • Итерация каждой IGrouping<'a, ValueSetting> группы из IEnumerable<IGrouping<'a, ValueSetting>> групп.
  • Вывод ключа вместе со сгруппированными значениями param4, объединенными в ", " строка с разделителями, использующая System.String.Join и System.Linq.Enumerable.Select.

Мы также можем просто сохранить param4 в сгруппированных значениях:

var grouped = list
    .GroupBy(x => new { x.param1, x.param2, x.param3 })
    .Select(grp => new { grp.Key, param4 = grp.Select(x => x.param4) });

foreach (var grp in grouped)
{
    Console.WriteLine($"{grp.Key} => ({string.Join(", ", grp.param4)})");
}

Выход:

{ param1 = 10, param2 = foo, param3 = abc } => (500, 600)
{ param1 = 20, param2 = boo, param3 = abc } => (500, 600)
{ param1 = 30, param2 = foo, param3 = abc } => (500)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...