如何检测哈希中的重复键并将前缀添加到重复项?

时间:2019-02-14 18:31:19

标签: ruby

我有两个数组,我正在使用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 :(得分:3)

看起来很简单附带了代码段

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"}

答案 1 :(得分:0)

基本上,您只需要在构建散列时跟踪散列的状态,并在发现冲突时创建一个新密钥。这捕获了一般方法:

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

(不要这样做。)

答案 2 :(得分:0)

这真的很简单。

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个John,这就是while循环位于函数内部的原因。

答案 3 :(得分:0)

使用zipeach_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

答案 4 :(得分:0)

这是创建所需哈希的一种方法。请注意,在arr1中,“约翰”出现了3次。

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_indexEnumerable#group_byHash#transform_values 1 Array#values_atv.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中的新功能。

答案 5 :(得分:0)

此代码可读性较差,但紧凑而实用。

从概念上讲,它与rahul mishra代码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) }