Dart: каков правильный способ суммирования элементов в списке карт?

#algorithm #sorting #dart

#алгоритм #сортировка #dart

Вопрос:

Я пытаюсь выяснить, какой наилучший способ суммировать все те же количества элементов, в частности, данные формируются следующим образом:

 data = [
          {Item Name: Item 2, Quantity: 1}, 
          {Item Name: Item 1, Quantity: 1}, 
          {Item Name: Item 3, Quantity: 1}, 
          {Item Name: Item 2, Quantity: 2}, 
          {Item Name: Item 1, Quantity: 2}, 
          {Item Name: Item 3, Quantity: 2},
       ];
  

и чего я пытаюсь достичь, так это:

 totalList = [{Item Name: Item 1, Quantity: 3}, {Item Name: Item 2, Quantity: 3}, {Item Name: Item 3, Quantity: 3}];
  

Я пытался использовать переменную TempData для удержания элемента и сравнения остальных, однако, похоже, что она сравнивает только этот первый элемент с остальной частью списка.

 var tempData = {};
var totalList = [];
data.forEach((element) {
  if (tempData.isEmpty) {
    tempData = element;
    totalList.add(tempData);
  } else {
    if (tempData['Item Name'] == element['Item Name']) {
       tempData['Quantity'] = tempData['Quantity']   element['Quantity'];
       totalList.add(tempData);
      } else {
        tempData = {
         'Item Name': element['Item Name'],
         'Quantity': element['Quantity']
     };
      totalList.add(tempData);
   }
  }
});
  

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

Что я должен сделать вместо этого?

Заранее спасибо за вашу помощь.

Комментарии:

1. Какой результат это дает вам?

Ответ №1:

Ваша структура данных не очень хороша; метки 'Item Name' и 'Quantity' не очень полезны в самой структуре, поэтому я бы избавился от них и создал упрощенный Map<String, int> , который напрямую сопоставляет имена с количествами. В идеале вы могли бы просто использовать упрощенную структуру с этого момента, но если вам действительно нужны явные метки, вы могли бы преобразовать обратно.

 void main(List<String> args) async {
  var data = [
    {'Item Name': 'Item 2', 'Quantity': 1},
    {'Item Name': 'Item 1', 'Quantity': 1},
    {'Item Name': 'Item 3', 'Quantity': 1},
    {'Item Name': 'Item 2', 'Quantity': 2},
    {'Item Name': 'Item 1', 'Quantity': 2},
    {'Item Name': 'Item 3', 'Quantity': 2},
  ];

  // Sum everything into a simpler data structure.
  var totalCounts = <String, int>{};
  for (var map in data) {
    var name = map['Item Name'] as String;
    var quantity = map['Quantity'] as int;
    totalCounts[name] = (totalCounts[name] ?? 0)   quantity;
  }

  // Reformat back into the original structure.
  var totalList = <Map<String, dynamic>>[
    for (var entry in totalCounts.entries)
      {'Item Name': entry.key, 'Quantity': entry.value},
  ];
  
  // Optional: Sort.
  totalList.sort((a, b) => a['Item Name'].compareTo(b['Item Name']));

  print(totalList); // Prints: [{Item Name: Item 1, Quantity: 3}, {Item Name: Item 2, Quantity: 3}, {Item Name: Item 3, Quantity: 3}]
}
  

В реальном коде я бы дополнительно добавил:

 const nameLabel = 'Item Name';
const quantityLabel = 'Quantity';
  

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

Ответ №2:

Я создал следующее решение, которое не такое красивое, но оно работает. Концепция заключается в создании Map<String, Map<String, Object>> , который отслеживает элементы, которые мы уже посетили, используя «Название элемента» каждого элемента в качестве ключа.

 void main() {
  final data = [
    {'Item Name': 'Item 2', 'Quantity': 1},
    {'Item Name': 'Item 1', 'Quantity': 1},
    {'Item Name': 'Item 3', 'Quantity': 1},
    {'Item Name': 'Item 2', 'Quantity': 2},
    {'Item Name': 'Item 1', 'Quantity': 2},
    {'Item Name': 'Item 3', 'Quantity': 2},
  ];

  final result = [
    ...data.fold(
        <String, Map<String, Object>>{},
        (Map<String, Map<String, Object>> sum, element) => sum
          ..update(
              element['Item Name'] as String,
              (value) => value
                ..update('Quantity',
                    (value) => (value as int)   (element['Quantity'] as int)),
              ifAbsent: () => Map.from(element))).values
  ];

  result.sort(
      (a, b) => (a['Item Name'] as String).compareTo(b['Item Name'] as String));

  print(result); // [{Item Name: Item 1, Quantity: 3}, {Item Name: Item 2, Quantity: 3}, {Item Name: Item 3, Quantity: 3}]
}