Как обнаружить дубликаты ключей в хэше и добавить префикс к дубликату? - PullRequest
0 голосов
/ 14 февраля 2019

У меня есть два массива, и я создаю пару ключ-значение, используя хэш в Ruby.Как я могу обнаружить дубликат ключа при сжатии двух массивов в пару ключ-значение и добавлении префикса типа «A-» перед именем ключа для дубликатов?

Я использую .zip для объединения двухмассивы и присвоение одному ключу, а другому - значения

[0] = "David"
[1] = "John"
[2] = "Alex"
[3] = "Sam"
[4] = "Caleb"
[5] = "David"
[6] = "John"
[7] = "Alex"
[8] = "Sam"

[0] = "1"
[1] = "2"
[2] = "3"
[3] = "4"
[4] = "5"
[5] = "6"
[6] = "7"
[7] = "8"
[8] = "9"


name_number_key_value_pair_hash = first_names.zip(numbers).to_h
puts(name_number_key_value_pair_hash)

Ожидаемое: {"David"=>"1", "John"=>"2", "Alex"=>"3", "Sam"=>"4", "Caleb"=>"5", "A-David"=>"6", "A-John"=>"7", "A-Alex"=>"8", "A-Sam"=>"9"} Фактическое: {"David"=>"6", "John"=>"7", "Alex"=>"8", "Sam"=>"9", "Caleb"=>"5"}

Ответы [ 6 ]

0 голосов
/ 15 февраля 2019

Этот код менее читаемый, но компактный и функциональный.

Концептуально он такой же, как и код рахула Мишры https://stackoverflow.com/a/54697573/2109121

names = %w[David John Alex Sam Caleb David John Alex Sam]
numbers = %w[1 2 3 4 5 6 7 8 9]

result = names.zip(numbers).reduce({}) { |a, (b, c)| a.merge(a.key?(b) ? "A-#{b}" : b => c) }
0 голосов
/ 14 февраля 2019

Это один из способов создать желаемый хэш.Обратите внимание, что в arr1 «Джон» появляется три раза.

arr1 = ["David", "John", "Alex", "Sam", "Caleb",
        "David", "John", "Alex", "John", "Sam"]
arr2 = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]

prefixes =
  arr1.each_with_object({}) do |s,h|
    if h.key?(s)
      prefix = "A-"
      (h[s].size-1).times { prefix = prefix.next } 
      h[s] << prefix
    else
      h[s] = ['']
    end
  end 
    #=> {"David"=>["", "A-"], "John"=>["", "A-", "B-"],
    #    "Alex"=>["", "A-"], "Sam"=>["", "A-"],
    #    "Caleb"=>[""]} 

arr1.map { |s| "#{prefixes[s].shift}#{s}" }.zip(arr2).to_h
  #=> {"David"=>"1", "John"=>"2", "Alex"=>"3", "Sam"=>"4",
  #    "Caleb"=>"5", "A-David"=>"6", "A-John"=>"7",
  #    "A-Alex"=>"8", "B-John"=>"9", "A-Sam"=>"10"} 

Обратите внимание, что "A-".next #=> "B-" и "Z-".next #=> "AA-".

Альтернативная структура данных

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

{"David"=>["1", "6"], "John"=>["2", "7", "9"],
 "Alex" =>["3", "8"], "Sam" =>["4", "10"], "Caleb"=>["5"]} 

Вы можете сделать это следующим образом.

arr1.each_with_index.
     group_by(&:first).
     transform_values { |v| arr2.values_at(*v.map(&:last)) }
  #=> {"David"=>["1", "6"], "John"=>["2", "7", "9"],
  #    "Alex" =>["3", "8"], "Sam" =>["4", "10"],
  #    "Caleb"=>["5"]} 

См. Enumerable # each_with_index , Enumerable # group_by , Hash # transform_values ​​ 1 и Array # values_at .v.map(*:last) здесь совпадает с v.map { |arr| arr.last }.Шаги следующие:

a = arr1.each_with_index
  #=> #<Enumerator: ["David", "John", "Alex", "Sam",
  #     "Caleb", "David", "John", "Alex", "John", "Sam"]:
  #     each_with_index>

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

a.to_a
  #=> [["David", 0], ["John", 1], ["Alex", 2], ["Sam", 3],
  #    ["Caleb", 4], ["David", 5], ["John", 6], ["Alex", 7],
  #    ["John", 8], ["Sam", 9]]

Продолжение,

b = a.group_by(&:first)
  #=> {"David"=>[["David", 0], ["David", 5]],
  #    "John"=> [["John",  1], ["John",  6], ["John", 8]],
  #    "Alex"=> [["Alex",  2], ["Alex",  7]],
  #    "Sam"=>  [["Sam",   3], ["Sam",   9]],
  #    "Caleb"=>[["Caleb", 4]]} 
b.transform_values { |v| arr2.values_at(*v.map(&:last)) }
  #=> {"David"=>["1", "6"], "John"=>["2", "7", "9"],
  #    "Alex"=> ["3", "8"], "Sam"=> ["4", "10"], "Caleb"=>["5"]} 

На последнем шаге первое значение хэша b передается блоку, и этому значению присваивается переменная блока.

v = b.values.first
  #=> [["David", 0], ["David", 5]]

Затем вычисления блока выполняются следующим образом.

c = v.map(&:last)
  #=> [0, 5] 
arr2.values_at(*c)
  #=> arr2.values_at(0, 5)
  #=> ["1", "6"]

Расчеты аналогичны для каждого из оставшихся значений b, которые передаются в блок.

1.Новое в Ruby MRI v2.4.

0 голосов
/ 14 февраля 2019

Использование zip и each_with_object

names   = %w[David John Alex Sam Caleb David John Alex Sam]
numbers = %w[1 2 3 4 5 6 7 8 9] 

names.zip(numbers).each_with_object({}) do |(name, number), hash|
  key          = hash.key?(name) ? "A-#{name}" : name
  hash[key]    = number 
end
0 голосов
/ 14 февраля 2019

Это действительно очень просто.

names = ["John","John", "John", "David", "David", "Susan", "Sue"]
numbers = ["1", "2", "3", "4", "5", "6","7"]

def uniq_hash_keys(names, numbers)
  hash = {}
  names.each_with_index do |name,i|
    if hash[name]
      prefix = 'A1-'
      key = prefix + name 
      while hash[key]
        version = prefix.match(/A(\d+)-.*/i)[1].to_i
        prefix = "A#{version + 1}-"
        key = prefix + name 
      end 
      name = key 
    end 
    hash[name] = numbers[i] 
  end 
  hash 
end 

Эта функция производит:

{
  "John"=>"1", 
  "A1-John"=>"2",
  "A2-John"=>"3",
  "David"=>"4",
  "A1-David"=>"5",
  "Susan"=>"6",
  "Sue"=>"7"
}

Обратите внимание, что есть 3 Джона, именно поэтому цикл while находится внутри функции.

0 голосов
/ 14 февраля 2019

Кажется, прямо вперед Прикрепили фрагмент кода

names = %w[David John Alex Sam Caleb David John Alex Sam]
numbers = %w[1 2 3 4 5 6 7 8 9] 

key_pair = {}
names.each_with_index do |name, index|
  name = "A-#{name}" if key_pair[name]
  key_pair[name] = numbers[index]
end

Он генерирует ожидаемый результат:

{"David"=>"1", "John"=>"2", "Alex"=>"3", "Sam"=>"4", "Caleb"=>"5", "A-David"=>"6", "A-John"=>"7", "A-Alex"=>"8", "A-Sam"=>"9"}
0 голосов
/ 14 февраля 2019

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

def hash_with_prefixes(a, b, prefixes)
    kv_pairs = a.zip(b)
    prefixes = prefixes.to_enum
    result_hash = {}

    kv_pairs.each do |initial_key, value|
        final_key = initial_key

        while result_hash.include? final_key
            final_key = "#{pfx.next}-#{initial_key}"
        end

        prefixes.rewind
        result_hash[final_key] = value
    end

    result_hash
rescue StopIteration
    fail "Insufficient prefixes to provide unique keys for input lists."
end

За счет ясности незначительный вы также можете записать его в более короткой форме:

def hash_with_prefixes(a, b, prefixes)
    pi = Hash[a.map {|k| [k, prefixes.lazy.map {|p| "#{p}-#{k}"}]}] 
    a.zip(b).inject({}) {|h, kv| h[h.include?(kv[0]) ? pi[kv[0]].next : kv[0]] = kv[1]; h}
rescue StopIteration
    fail "Insufficient prefixes to provide unique keys for input lists."
end

(Донне делай этого.)

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