Ruby 在特定键的哈希数组中查找重复项

Ruby 在特定键的哈希数组中查找重复项,ruby,arrays,csv,hash,duplicates,Ruby,Arrays,Csv,Hash,Duplicates,我有一个散列数组(实际上是CSV行),我需要找到并保留与两个特定键(user,section)匹配的所有行。以下是数据示例: [ { user: 1, role: "staff", section: 123 }, { user: 2, role: "staff", section: 456 }, { user: 3, role: "staff", section: 123 }, { user: 1, role: "exec", section: 123 }, { user:

我有一个散列数组(实际上是CSV行),我需要找到并保留与两个特定键(user,section)匹配的所有行。以下是数据示例:

[
  { user: 1, role: "staff", section: 123 },
  { user: 2, role: "staff", section: 456 },
  { user: 3, role: "staff", section: 123 },
  { user: 1, role: "exec", section: 123 },
  { user: 2, role: "exec", section: 456 },
  { user: 3, role: "staff", section: 789 }
]
因此,我需要返回一个数组,该数组只包含同一个user/section组合多次出现的行,如下所示:

[
  { user: 1, role: "staff", section: 123 },
  { user: 1, role: "exec", section: 123 },
  { user: 2, role: "staff", section: 456 },
  { user: 2, role: "exec", section: 456 }
]
我尝试的双循环解决方案如下所示:

enrollments.each_with_index do |a, ai|
  enrollments.each_with_index do |b, bi|
    next if ai == bi

    duplicates << b if a[2] == b[2] && a[6] == b[6]
  end
end
enrollments.each_与_索引do|a,ai|
注册。每个|u都有|u索引do | b,bi|
下一步如果ai==bi

重复要在内存中执行此检查,您不需要双循环,您可以保留一个唯一值数组,并对照它检查每个新csv行:

found = []
unique_enrollments = []

CSV.foreach('/path/to/csv') do |row|
  # do whatever you're doing to parse this row into the hash you show in your question:
  # => { user: 1, role: "staff", section: 123 }
  # you might have to do `next if row.header_row?` if the first row is the header

  enrollment = parse_row_into_enrollment_hash(row)
  unique_tuple = [enrollment[:user], enrollment[:section]]

  unless found.include? unique_tuple
    found << unique_tuple
    unique_enrollments << enrollment
  end
end
通过上述调整,您可以通过不保留大量注册来节省内存。虽然缺点是,如果某个东西爆炸了,你将无法回滚。例如,如果我们完成了前者,并在最后保留了一个
唯一注册的数组,那么您可以执行以下操作:

Enrollment.transaction do
  unique_enrollments.each &:save!
end
现在,如果任何一次保存失败,您都可以回滚。此外,在单个
事务中包装一组db调用要快得多。我会采用这种方法

编辑:使用
唯一注册的数组
您可以在最后迭代这些内容并创建新的CSV:

CSV.open('path/to/new/csv') do |csv|
  csv << ['user', 'role', 'staff'] # write the header

  unique_enrollments.each do |enrollment|
    csv << enrollment.values # just the values not the keys
  end
end
CSV.open('path/to/new/CSV')do | CSV|

csv就效率而言,您可能希望尝试以下方法:

grouped = csv_arr.group_by{|row| [row[:user],row[:section]]}
filtered = grouped.values.select { |a| a.size > 1 }.flatten
第一条语句按
:user
:section
键对记录进行分组。结果是:

{[1, 123]=>[{:user=>1, :role=>"staff", :section=>123}, {:user=>1, :role=>"exec", :section=>123}],
 [2, 456]=>[{:user=>2, :role=>"staff", :section=>456}, {:user=>2, :role=>"exec", :section=>456}],
 [3, 123]=>[{:user=>3, :role=>"staff", :section=>123}],
 [3, 789]=>[{:user=>3, :role=>"staff", :section=>789}]}
第二条语句仅选择具有多个成员的组的值,然后将结果展平,以提供:

[{:user=>1, :role=>"staff", :section=>123},
 {:user=>1, :role=>"exec", :section=>123},
 {:user=>2, :role=>"staff", :section=>456},
 {:user=>2, :role=>"exec", :section=>456}]

这可以提高您的操作速度,但在内存方面,我不能说大输入会产生什么影响,因为这取决于您的机器、资源和文件大小

您使用什么读取CSV
CSV.foreach
应该一次读取一行,这将有利于内存消耗。但无论如何,您都需要比较所有行。您可以在内存中完成这些操作,也可以保存到数据库并将其保存到唯一性搜索。我正在将CSV读取到数组中。所以你建议只在阅读时进行比较?像两个嵌套的
CSV.foreach
块吗?绝对不是两个
CSV.foreach
调用。请参阅答案。没有数据库。我只是想从本地CSV获取我需要的数据。我还认为我的样本数据有点混乱。数据未被触及,但我只是比较了某些字段,我想不被触及地存储它。基本上是1。加载2。执行检查3。输出符合检查条件的行。哦,那很好,只需保存所有唯一的行并使用它们写回新的CSV。这正是我所需要的,只是我刚刚发现提供给我的数据中有一些垃圾。下一个任务是找出如何清理它,以便我可以使用它。谢谢
[{:user=>1, :role=>"staff", :section=>123},
 {:user=>1, :role=>"exec", :section=>123},
 {:user=>2, :role=>"staff", :section=>456},
 {:user=>2, :role=>"exec", :section=>456}]