Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

Adding Array.uniq_by(proc) to core_ext, allowing selection of unique elements based on their response to a proc

#2957

Oftentimes it's necessary to select some unique elements from an array based on various attributes. For instance, if you have an array of posts and you want to select only posts that have a unique author_id; or posts with a unique combination of author_id and created_at date to get an array with one post per user per day. It's usually possible to do this with SQL when you're dealing with AR objects, but that's not always an option, so I wrote this patch to address those cases.

    array = [{:id => 1, :val => 1}, 
             {:id => 2, :val => 1},
             {:id => 3, :val => 2},
             {:id => 4, :val => 3}]
             
    array.uniq_by(Proc.new {|a| a[:val]}).map{|a| a[:id]} == [1,3,4]

Reported by Joey Schoblaska · July 25th, 2009 @ 11:46 PM

State: resolved
Milestone: 3.0.2
Assigned to: José Valim José Valim
Importance: none

Activity

  1. Joey Schoblaska
    Joey Schoblaska

    I can't download the original attachment, so trying another upload to see if that fixes it.

    July 27th, 2009 @ 04:17 PM

  2. Joey Schoblaska
  3. Yehuda Katz (wycats)
    Yehuda Katz (wycats)
    • State changed from new to open
    • Assigned user set to Yehuda Katz (wycats)
    • Milestone cleared.

    Looks good to me. Are there any parts of the Rails internals that you could see benefiting from this patch?

    July 28th, 2009 @ 03:13 AM

  4. Joey Schoblaska
    Joey Schoblaska

    Thanks! I can't think of any off the top of my head, but I'll definitely take a look.

    July 28th, 2009 @ 04:41 AM

  5. Elliot Winkler
    Elliot Winkler

    +1 for the idea. I wrote a version of the method a while back (except using inject + a hash instead of a select within a select as you've got). It does come in handy.

    Question: Why does your uniq_by accept a Proc object? Shouldn't it just accept a block? I.e.:

    arr.uniq_by {|x| ... }
    

    as opposed to

    arr.uniq_by(Proc.new {|x| ... })
    

    July 28th, 2009 @ 07:29 PM

  6. Joey Schoblaska
    Joey Schoblaska

    Thanks for the suggestion. You're right - blocks result in much nicer-looking method calls than procs. I updated the patch to use blocks instead.

    July 29th, 2009 @ 01:33 AM

  7. Pratik
    Pratik

    Why not just override Array#uniq and uniq! instead of adding a new method ?

    July 31st, 2009 @ 10:45 PM

  8. Joey Schoblaska
    Joey Schoblaska

    It seemed like overriding a Ruby method as common as [].uniq might cause confusion so I opted to create a new, explicitly-named method instead.

    August 1st, 2009 @ 12:27 AM

  9. Pratik
    Pratik

    I think doing something like this might be ok :

      def uniq
        if block_give?
          select {|item| select {|i| yield(i) == yield(item)}.index(item) == 0 }
        else
          super
        end
      end
    

    Just a thought. I'm fine with a new method too. I just think it's more intuitive to make uniq accept a block.

    August 1st, 2009 @ 12:47 AM

  10. Dan Barry
    Dan Barry

    Joey's implementation O(n^3 / m) where n is the number of elements in the array and m is the number of unique values. I came up with a different implementation that is more performant: http://gist.github.com/159799

    August 1st, 2009 @ 09:53 PM

  11. Cheah Chu Yeow
    Cheah Chu Yeow

    I like Pratik's idea of overriding uniq. There've been more than a few times that I've used uniq expecting that it takes a block.

    Otherwise I like the general idea of this patch :)

    August 3rd, 2009 @ 02:21 AM

  12. Evgeniy Dolzhenko
    Evgeniy Dolzhenko

    +1 since was always using the same method for a long time available in Facets http://facets.rubyforge.org/git?p=facets.git;a=blob_plain;f=lib/cor...

    Also if you guys came with more efficient implementation I think it's worth sending it to Facets as well.

    August 10th, 2009 @ 10:24 AM

  13. Evgeniy Dolzhenko
    Evgeniy Dolzhenko

    Ok, the Facets link got crippled, here is the shortened one http://bit.ly/QMjcp

    August 10th, 2009 @ 10:28 AM

  14. jack dempsey (jackdempsey)
    jack dempsey (jackdempsey)

    +1 I could use something like this right now. Dan's patch is nice as well--those speed improvements are excellent.

    October 28th, 2009 @ 03:28 PM

  15. Dan Peterson
    Dan Peterson

    Just discovered this ticket while Googling for my version of Enumerable#uniq_by. ;)

    Here's the version I came up with if it helps: http://gist.github.com/168544

    December 10th, 2009 @ 09:46 PM

  16. José Valim
    José Valim
    • State changed from open to resolved
    • Assigned user changed from Yehuda Katz (wycats) to José Valim

    That was already added on master.

    February 3rd, 2010 @ 09:56 AM

  17. Jeremy Kemper
    Jeremy Kemper
    • Milestone set to 3.0.2
    • Importance changed from to

    October 15th, 2010 @ 11:01 PM