Удалите дубликаты из массива в Ruby и выполните операцию с указанным c индексом - PullRequest
0 голосов
/ 28 января 2020

Мне нужно удалить дубликаты из массива в Ruby и выполнить операцию с указанным индексом c при удалении дубликатов. Позвольте мне объяснить на примере:

arr = [["A", "Red", 7], ["A", "Red", 8], ["B", "Red", 3],["B", "Blue", 2],
       ["B", "Blue", 3], ["C", "Blue", 3], ["C", "Black", 1], ["D", nil, 4],
       ["D", nil, 5]]

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

Так что мне нужен вывод:

[["A", "Red", 15],  ["B", "Red", 3],["B", "Blue", 5], ["C", "Blue", 3],
 ["C", "Black", 1], ["D", nil, 9]]

Ответы [ 3 ]

3 голосов
/ 28 января 2020

Для этого можно использовать метод Enumerable#group_by в сочетании с Enumerable#map и Array#sum.

arr = [["A", "Red", 7], ["A", "Red", 8], ["B", "Red", 3],["B", "Blue", 2], ["B", "Blue", 3], ["C", "Blue", 3], ["C", "Black", 1], ["D", nil, 4], ["D", nil, 5]]

result = arr.group_by { |*keys, _number| keys }
            .map { |keys, items| keys << items.sum(&:last) }
#=> [["A", "Red", 15], ["B", "Red", 3], ["B", "Blue", 5], ["C", "Blue", 3], ["C", "Black", 1], ["D", nil, 9]]
0 голосов
/ 28 января 2020
arr.each_with_object(Hash.new(0)) { |(*a,n),h| h[a] += n }.map(&:flatten)
  #=> [["A", "Red", 15], ["B", "Red", 3], ["B", "Blue", 5], ["C", "Blue", 3],
  #    ["C", "Black", 1], ["D", nil, 9]]

Первый шаг вычисления:

h = arr.each_with_object(Hash.new(0)) { |(*a,n),h| h[a] += n }
  #=> {["A", "Red"]=>15, ["B", "Red"]=>3, ["B", "Blue"]=>5,
  #    ["C", "Blue"]=>3, ["C", "Black"]=>1, ["D", nil]=>9}

Используется форма Ха sh :: new , которая принимает аргумент, называемый значением по умолчанию. Все это означает, что когда парсер Ruby расширяется h[a] += 1 до

h[a] = h[a] + n

h[a], справа возвращается значение по умолчанию h, 0, если h делает нет ключа a. Например, когда h пусто,

h[["A", "Red"]] = h[["A", "Red"]] + 7 #=> 0 + 7 =>  7
h[["A", "Red"]] = h[["A", "Red"]] + 8 #=> 7 + 8 => 15

h не имеет ключа ["A", "Red"] в первом выражении, поэтому h[["A", "Red"]] справа возвращает значение по умолчанию, 0 тогда как h имеет этот ключ во втором выражении, поэтому значение по умолчанию не применяется.

h.map(&:flatten) является сокращением для

h.map { |a| a.flatten }

Когда переменная блока a имеет значение установить равным первой паре ключ-значение h,

a #=> [["A", "Red"], 15]

Итак

a.flatten
  #=> ["A", "Red", 15]

Чтобы понять |(*a,n),h|, нам нужно построить перечислитель

enum = arr.each_with_object(Hash.new(0))
  #=> #<Enumerator: [["A", "Red", 7], ["A", "Red", 8], ["B", "Red", 3],
  #     ["B", "Blue", 2], ["B", "Blue", 3], ["C", "Blue", 3],
  #     ["C", "Black", 1], ["D", nil, 4], ["D", nil, 5]]
  #     :each_with_object({})> 

Теперь мы сгенерируем первое значение из перечислителя (используя Enumerator # next ) и присвоим значения переменным блока:

(*a,n),h = enum.next
  #=> [["A", "Red", 7], {}] 
a #=> ["A", "Red"] 
n # => 7 
h #=> {} 

Способ возврата массива enum.next разбивается на составляющие элементы, которые присваиваются переменным блока, называется декомпозиция массива . Это мощная и очень полезная техника.

0 голосов
/ 28 января 2020

Вы можете использовать each_with_object и pu sh первые 2 элемента в качестве ключа к объекту ha sh:

new_arr = arr.each_with_object(Hash.new{ |h,k| h[k] = 0 }) do |val, hash|
  hash[val[0..1]] += val[-1]
end.map(&:flatten)

new_arr #=> [["A", "Red", 15],
         #=> ["B", "Red", 3],
         #=> ["B", "Blue", 5],
         #=> ["C", "Blue", 3],
         #=> ["C", "Black", 1],
         #=> ["D", nil, 9]]

или просто each с Hash объект, определенный по умолчанию для pro c: Hash.new{ |h,k| h[k] = 0 }

Это, конечно, не будет работать, если ваши значения в подмассивах отличаются, например, ["A", "Red", 5] и ["A", "red", 7] будут относиться по-разному.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...