如何检测哈希中的重复键并在重复方面添加前缀



我有两个数组,我正在使用Ruby中的Hash创建一个键值值。在将两个阵列拉入键值对并在重复器的密钥名称前添加诸如" 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"}

看来直接已附加了代码段

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

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

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

(不要这样做。)

这真的很简单。

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循环在功能内部的原因。

这是创建所需哈希的一种方法。请注意,在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"]} 

请参阅枚举#every_with_index,enumerable#group_by,hash#transform_values 1 和数组#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中的新事物。

此代码不那么可读,但紧凑且功能风格。

概念上与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) }

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

最新更新