Remove Duplicate Hashes From An Array By A Specific Key In Ruby
uniq on an array of hashes compares whole hashes, so two records that share an id but differ in any other field both survive. Deduplicating by one key — an id, an email, a SKU — needs uniq with a block.
Description
uniq accepts a block; the block’s return value is what gets compared. uniq { |h| h[:id] } keeps the first occurrence of each id and discards later ones.
To keep the last occurrence instead, reverse before and after, or build a hash keyed on the field and take values — the latter is clearer and does one pass.
For composite keys, return an array from the block. For case-insensitive deduplication, normalize inside the block rather than mutating the data.
Sample input:
records = [
{ id: 1, name: "Ada", role: "admin" },
{ id: 2, name: "Grace", role: "user" },
{ id: 1, name: "Ada L", role: "owner" }
]
Sample Output:
# => [{ id: 1, name: "Ada", role: "admin" }, { id: 2, name: "Grace", role: "user" }]
Answer
records.uniq { |r| r[:id] }
# => keeps the first occurrence of each id
# Keep the LAST occurrence instead
records.reverse.uniq { |r| r[:id] }.reverse
# Same thing, one pass, clearer intent
records.each_with_object({}) { |r, acc| acc[r[:id]] = r }.values
# Composite key
records.uniq { |r| [r[:account_id], r[:sku]] }
# Case-insensitive on a string key
records.uniq { |r| r[:email].to_s.downcase.strip }
# Which ids were duplicated?
records.group_by { |r| r[:id] }
.select { |_, group| group.size > 1 }
.keys
# => [1]
Check viewARU - Brand Newsletter!
Newsletter to DEVs by DEVs - boost your Personal Brand & career! 🚀