Несколько ключей в словаре после группировки из таблицы данных c # - PullRequest
0 голосов
/ 29 декабря 2018

В настоящее время я извлекаю несколько столбцов из Excel.

Формат Excel похож на ниже

 StudentId   StudentTempId    Department    Address       TotalMarks
 1           100              IT            Brooklyn      90
 1           100              IT            Manhattan     80
 2           200              HR            Boston        50

У одного учащегося может быть несколько адресов, поэтому после извлечения данных из Excel в таблицу данныхСначала я применяю группу, а затем преобразовываю ее в словарь.Я применил группу по 2 столбцам StudentId и StudentTempId.Из-за чего создаются два ключа при преобразовании в словарь.На данный момент я создал кортеж, имеющий те же два элемента и остальные 3 поля, которые я добавляю в значение словаря в виде класса StudentDetail, который выглядит следующим образом:

public class StudentDetail
{
    public string Department { get; set; }
    public string Address { get; set; }
    public int TotalMarks { get; set; }
}

Ниже кода, который я написалЧтобы заполнить все данные Excel в объекте:

Dictionary<Tuple<int, int>, List<StudentDetail>> StudentDetailList 
      = dataTable.AsEnumerable()
      .GroupBy(row => Tuple.Create
       (
          row.Field<int>("StudentId"),
          row.Field<int>("StudentTempId")
       )).
       ToDictionary
       (
          dict => dict.Key,
          dict => dict.Select(row => new StudentDetail
          {
             Department = row.Field<string>("Department"),
             Address = row.Field<string>("Address"),
             TotalMarks = row.Field<int>("TotalMarks")
          }).ToList()
        );

Есть ли другой хороший способ, кроме использования Tuple в качестве комбинации клавиш?

Любая помощь?

Ответы [ 3 ]

0 голосов
/ 29 декабря 2018

Предполагая, что вы не хотите, чтобы StudentId и StudentTempId были частью модели StudentDetail, я бы сказал, что это хороший подход, поскольку вы можете просто получить соответствующий список по ключу с помощью:

var studentDetails = StudentDetailList[Tuple.Create(1, 100)];

Вы можете сделать синтаксис немного лучше, сгруппировавшись по ValueTuple т.е.

.GroupBy(row => (row.Field<int>("StudentId"),
                    row.Field<int>("StudentTempId")))

В этом случае вы получите к нему доступ:

var studentDetails = StudentDetailList[(1, 100)];

Еслиоднако, у вас все в порядке с наличием вышеупомянутых свойств в StudentDetail, то есть StudentId и StudentTempId, тогда вы можете получить List<IEnumerable<StudentDetail>>, выполнив:

var studentDetails = dataTable.AsEnumerable()
            .GroupBy(row =>
            (
                row.Field<int>("StudentId"),
                row.Field<int>("StudentTempId")
            )).Select(g => g.Select(row => new StudentDetail
            {
                StudentId = g.Key.Item1,
                StudentTempId = g.Key.Item2,
                Department = row.Field<string>("Department"),
                Address = row.Field<string>("Address"),
                TotalMarks = row.Field<int>("TotalMarks")
            })).ToList();

Всякий раз, когда вам нужно найтиВы можете запросить определенную группу данных о студентах как:

var result = studentDetails.SingleOrDefault(s => s.Any(e => e.StudentId == 1 && e.StudentTempId == 100));

или используя FirstOrDefault, в зависимости от того, что вы считаете наиболее подходящим:

var result = studentDetails.FirstOrDefault(s => s.Any(e => e.StudentId == 1 && e.StudentTempId == 100));
0 голосов
/ 29 декабря 2018

Tuple подходит для комбинированных ключей, поскольку его реализация GetHashCode фактически использует все значения для генерации хеш-кода.Это также верно для ValueTuple.

Чего вы хотите избежать, так это использовать структуру без переопределения GetHashCode, поскольку она будет использовать реализацию, определенную в ValueType, которая просто вызывает GetHashCode с первого разазначение в структуре.Это может вызвать много коллизий, если несколько ключей имеют то же значение, что и их первый член.Это не нарушит ваш код, но уменьшит производительность словаря.

Вы можете посмотреть эти реализации здесь: https://referencesource.microsoft.com

Я предлагаю ValueTuple, если вы используете C # 7.0или выше, так как есть литерал, который вы можете использовать для него.https://blogs.msdn.microsoft.com/mazhou/2017/05/26/c-7-series-part-1-value-tuples/

0 голосов
/ 29 декабря 2018

Используйте UInt64 в качестве ключа для объединения целых чисел кортежа с использованием битовых сдвигов.

Dictionary<UInt64, List<StudentDetail>> StudentDetailList
 = dataTable.AsEnumerable()
  .GroupBy(row => new UInt64(       
      (UInt64)(row.Field<int>("StudentId"))<< 32 |
      (UInt64)(row.Field<int>("StudentTempId"));
   )).
   ToDictionary
   (
      dict => dict.Key,
      dict => dict.Select(row => new StudentDetail
      {
         Department = row.Field<string>("Department"),
         Address = row.Field<string>("Address"),
         TotalMarks = row.Field<int>("TotalMarks")
      }).ToList()
    );

Битовые операции свойственны современным процессорам и должны приводить к увеличению производительности (при условии 64-битнойпроцесс и никаких отрицательных значений).Чтобы отделить ключ обратно, при необходимости используйте следующий код:

int StudentId = key >> 32;
int StudentTempId = key && 0xFFFFFFFF;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...