Ruby removing duplicates from array based on key=>value
Asked Answered
V

3

7

I have an array of Musical Tracks and in this array the same song can show up multiple times due to being released on multiple albums. I am trying to remove them from the array so that only true uniques show up in the list.

The Hash looks something like this:

"tracks" => [
    [0] {
        "id" => 1,
        "Title" => "Intergalactic",
        "ArtistName" => "Beastie Boys"
    },
    [1] {
        "id" => 2,
        "Title" => "Intergalactic",
        "ArtistName" => "Beastie Boys"
    }
]

I am needing a way to remove the duplicates based on the Title key. Anyway of doing this?

Vang answered 6/10, 2011 at 18:0 Comment(0)
D
11

If you are using ActiveSupport, you can use uniq_by, like so :

tracks.uniq_by {|track| track["title"]}

If not, then you can easily implement it yourself. See this.

# File activesupport/lib/active_support/core_ext/array/uniq_by.rb, line 6
  def uniq_by
    hash, array = {}, []
    each { |i| hash[yield(i)] ||= (array << i) }
    array
  end
Denice answered 6/10, 2011 at 18:8 Comment(1)
Thank you so much for the help!!!! Each day that I am doing Ruby development I begin to become a happier/better personVang
M
10

The Array#uniq! method in 1.9 takes a block so if your Hash is h then:

h['tracks'].uniq! { |x| x['Title'] }

If you're in 1.8 then you can fake it with:

h['tracks'] = h['tracks'].group_by { |x| x['Title'] }.values.map(&:first)

I'm assuming that you want to modify it in-place.

Mcevoy answered 6/10, 2011 at 18:45 Comment(4)
Ah ! I didn't know it was in 1.9, thanks for the tip. Obviously if you are using 1.9 this is the right answer.Denice
@DuoSRX: It doesn't seem to be that well known and it isn't exactly well documented (you have to infer it from the examples). A right answer is one that gets the job done :)Mcevoy
What if you need the entry to be unique by more than one value? I have the same situation, but need to check that Title, Artist and Composer be the same. With the uniq block I can only use one value!Blackguard
@kakubei: What is %w[a b] == %w[a b]? I suspect that's the solution to your problem.Mcevoy
C
0

While the other methods are correct, I'll throw in a bit of extra sugar I found elsewhere on SO.

Using this extension of Symbol:

class Symbol
  def with(*args, &block)
    ->(caller, *rest) { caller.send(self, *rest, *args, &block) }
  end
end

Instead of writing simple iterative blocks like

foo_array.each{ |foo| foo.update_bar("baz") }, you can now use

foo_array.each &:update_bar.with("baz")

Similar to how you might write maybe_nil.try(:[], "key")...

foo_array.uniq{ |foo| foo["key"] } is now identical to

foo_array.uniq(&:[].with("key"))

I hope this helps

Corneille answered 1/7, 2019 at 18:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.