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?

dennismonsewicz
  • 25,132
  • 33
  • 116
  • 189

3 Answers3

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
DuoSRX
  • 4,179
  • 3
  • 26
  • 32
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.

mu is too short
  • 426,620
  • 70
  • 833
  • 800
  • 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. – DuoSRX Oct 07 '11 at 07:40
  • @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 :) – mu is too short Oct 07 '11 at 07:45
  • 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! – kakubei Dec 13 '13 at 11:33
  • @kakubei: What is `%w[a b] == %w[a b]`? I suspect that's the solution to your problem. – mu is too short Dec 13 '13 at 14:12
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

Jesse Novotny
  • 704
  • 7
  • 16