Arrays 在散列中识别重复值的“Ruby方式”是什么?

Arrays 在散列中识别重复值的“Ruby方式”是什么?,arrays,ruby,hash,Arrays,Ruby,Hash,我在macOS Catalina上使用Ruby 2.7.x 我有多达一百万个键值元组。 键是字符串,并保证唯一。 这些值是字符串,可能包含重复项、三重项或更多项。 考虑到密钥的唯一性,哈希似乎是密钥的自然数据结构。 如果我从包含所有键值元组的原始_散列开始, 我想以uniques_hash结束,它包含所有且仅包含唯一的键值元组, 和复制\u散列,其中包含具有重复值的所有键 我更感兴趣的是优化清晰性和Ruby习惯,而不是内存效率或速度——我不希望经常运行这段代码,而且我有足够的RAM 如果我转换为

我在macOS Catalina上使用Ruby 2.7.x

我有多达一百万个键值元组。 键是字符串,并保证唯一。 这些值是字符串,可能包含重复项、三重项或更多项。 考虑到密钥的唯一性,哈希似乎是密钥的自然数据结构。 如果我从包含所有键值元组的原始_散列开始, 我想以uniques_hash结束,它包含所有且仅包含唯一的键值元组, 和复制\u散列,其中包含具有重复值的所有键

我更感兴趣的是优化清晰性和Ruby习惯,而不是内存效率或速度——我不希望经常运行这段代码,而且我有足够的RAM

如果我转换为两个数组,我可以在values数组中找到unique-但是我如何保证用正确的键重新配对呢?这是解决这个问题的正确方法吗


非常感谢您的帮助

这可能是最好的方法,也可能不是最好的方法,但我使用了group_by和select函数来获得一个新的哈希,该哈希可以查找重复项:

hash.group_by{|k,v| v}.select{|k,v| v.count > 1}
在这种情况下,返回的散列看起来有点像:

{value: [{key: value}, {key: value}]}
使用计算值并将结果存储在哈希中。将该散列用于原始散列,如下所示:

h = {a: 1, b: 2, c: 2, d: 2, e: 3, f: 4, g: 4}

cnt = Hash[h.values.group_by{ |i| i }.map { |k, v| [k, v.count] }]
h_uniq, h_dups = h.partition{ |k, v| cnt[v] == 1 }.map(&:to_h)

puts cnt
# {1=>1, 2=>3, 3=>1, 4=>2}

puts h_uniq.inspect
# {:a=>1, :e=>3}

puts h_dups.inspect
# {:b=>2, :c=>2, :d=>2, :f=>4, :g=>4}

假设

original_hash = {:a=>1, :b=>2, :c=>2, :d=>2, :e=>3, :f=>4, :g=>4}
如果您只想返回包含唯一值的散列uniques_散列,那么可以编写以下代码

uniques_hash = original_hash.invert.invert
  #=> {:a=>1, :d=>2, :e=>3, :g=>4}
{:a=>1, :b=>2, :e=>3, :f=>4}
{:a=>1, :b=>2, :e=>3, :g=>4}
{:a=>1, :c=>2, :e=>3, :f=>4}
{:a=>1, :c=>2, :e=>3, :g=>4}
{:a=>1, :d=>2, :e=>3, :f=>4}
{:a=>1, :d=>2, :e=>3, :g=>4}
中间步骤是

original_hash.invert
  #=> {1=>:a, 2=>:d, 3=>:e, 4=>:g}
看。请注意,定义的uniques_散列本身并不是唯一的。它可能是以下任何一种

uniques_hash = original_hash.invert.invert
  #=> {:a=>1, :d=>2, :e=>3, :g=>4}
{:a=>1, :b=>2, :e=>3, :f=>4}
{:a=>1, :b=>2, :e=>3, :g=>4}
{:a=>1, :c=>2, :e=>3, :f=>4}
{:a=>1, :c=>2, :e=>3, :g=>4}
{:a=>1, :d=>2, :e=>3, :f=>4}
{:a=>1, :d=>2, :e=>3, :g=>4}
另一种方法是使用和

中间计算正在进行中

original_hash.uniq(&:last)
  #=> [[:a, 1], [:b, 2], [:e, 3], [:f, 4]]
这是

original_hash.uniq { |_k,v| v }
假设,重复散列的每个键都是原始散列中的一个值,该键的值是原始散列中那些键k的数组,原始散列[k]==v

计算重复\u散列的一种方法如下所示

duplicates_hash = original_hash.each_with_object({}) do |(k,v),h|
  h[v] = (h[v] || []) << k
end
  #=> {1=>[:a], 2=>[:b, :c, :d], 3=>[:e], 4=>[:f, :g]}
keeper_keys = duplicates_hash.values.map(&:first)
  #=> [:a, :b, :e, :f] 
unique_keys = original_hash.slice(*keeper_keys)
  #=> {:a=>1, :b=>2, :e=>3, :f=>4} 
枚举数是类的实例

生成枚举数的第一个值,并为块变量赋值,如下所示:

(k,v),h = enum.next
  #=> [[:a, 1], {}]
可以看到数组分解将此数组拆分为两个元素,如下所示:

k #=> :a 
v #=> 1 
h #=> {} 
请注意左侧的括号与右侧的内括号是如何对应的。然后使用这些变量执行块计算

h[v] = (h[v] || []) << k
  #=> [:a]
然后,枚举器生成下一个值,并执行块计算

(k,v),h = enum.next
  #=> [[:b, 2], {1=>[:a]}] 
k #=> :b 
v #=> 2 
h #=> {1=>[:a]} 
h[v] = (h[v] || []) << k
这种情况一直持续到

enum.next
  #=> Stop Interation (exception)
使Ruby返回h的值

注意,通过首先计算重复的散列,我们可以按如下方式计算uniques散列

duplicates_hash = original_hash.each_with_object({}) do |(k,v),h|
  h[v] = (h[v] || []) << k
end
  #=> {1=>[:a], 2=>[:b, :c, :d], 3=>[:e], 4=>[:f, :g]}
keeper_keys = duplicates_hash.values.map(&:first)
  #=> [:a, :b, :e, :f] 
unique_keys = original_hash.slice(*keeper_keys)
  #=> {:a=>1, :b=>2, :e=>3, :f=>4} 

看。如果一个人因为偏爱某些键而感到内疚,他可以改为写

unique_keys = original_hash.slice(*duplicates_hash.values.map(&:sample))
  #=> {:a=>1, :b=>2, :e=>3, :g=>4}

请参阅。

谢谢大家在这方面的帮助!我想如果我发布了我的代码草案,可能会对某些人有所帮助,任何评论/改进都是非常受欢迎的!我正在重构清单。。 N


您是否有任何代码或正在查看的数据示例?请提供示例。如果你的散列是{:a=>1,:b=>1},你希望你的uniques_散列等于{:a=>1}还是{:b=>1}?贾德,卡里-谢谢参与!密钥是文件路径,例如photos/2009年5月3日/IMG_4588.jpg等等。我通过遍历目录结构生成文件路径列表,因此我知道它们是唯一的。这些值是十六进制摘要,由摘要::SHA512.hexdigest File.read从文件路径生成。因此,如果我的目录结构包含同一张照片的两个或多个副本,我将有重复的值。我要做的是最终将目录结构复制到一个包含每个文件的一个副本的目录结构,以及另一个只包含重复副本的目录结构@卡里-我不管是哪一个!卡里,非常感谢。双倒扣很聪明,剩下的我会处理好的。很明显,我是这方面的初学者!非常感谢帖木儿,太好了!jad-super,看起来像是“组员”把我带到了我想去的地方!
keeper_keys = duplicates_hash.values.map(&:first)
  #=> [:a, :b, :e, :f] 
unique_keys = original_hash.slice(*keeper_keys)
  #=> {:a=>1, :b=>2, :e=>3, :f=>4} 
unique_keys = original_hash.slice(*duplicates_hash.values.map(&:first))
  #=> {:a=>1, :b=>2, :e=>3, :f=>4}
unique_keys = original_hash.slice(*duplicates_hash.values.map(&:sample))
  #=> {:a=>1, :b=>2, :e=>3, :g=>4}
#!/usr/bin/env ruby
# shebang to run the script from Terminal

# include shasum
require 'digest'

class Listing
  # class of arrays of file listings 
attr_reader :path
  def initialize(path)
    @path = path
    Dir.chdir(@path)
    @list_of_items = Dir['**/*']
    @list_of_folders = []
    @list_of_files = []
    @list_of_extensions = []
    @list_of_uniques = []
    @list_of_duplicates = []
  end

  def to_s
    "#{@path}"
  end
  
  def analyse_items
    @list_of_items.each do |f|
      if File.directory?(f)
        @list_of_folders << f
      else
        @list_of_files << f
      end
    end
    @list_of_folders.sort!
    @list_of_files.sort!
    @folder_count = @list_of_folders.count
    @files_count = @list_of_files.count
    @items_count = @list_of_items.count
    @count_check = (@items_count -(@folder_count + @files_count))
    # count_check should be zero
  end

  def identify_duplicates
    # Given an array of filepaths, this method divides it into an array of unique files and an array of duplicated files.  
    source = {}
    uniques = {}
        
    @list_of_files.each do |f|
      digest = Digest::SHA512.hexdigest File.read f
      source.store(f, digest)
    end
    
    uniques = source.invert.invert
    @list_of_uniques = uniques.keys
    @list_of_duplicates = source.keys - uniques.keys
  end
  
  def tell_duplicates
    puts "dupes = #{@list_of_duplicates}"
  end
    
end
 


l = Listing.new("/Volumes/Things/Photos/")
l.analyse_items
l.identify_duplicates
l.tell_duplicates