compact or uniq that takes a block like sort?

Discussion in 'Ruby' started by Bil Kleb, Jan 7, 2008.

  1. Bil Kleb

    Bil Kleb Guest

    Hi,

    I'm looking for an elegant way to do the following:

    Given an sorted array of (X,Y) pairs where Y is strictly
    increasing, i.e., no repeated Ys.

    Compact pairs with repeated Xs, keeping the pair with
    the highest Y value.

    E.g.,

    require 'test/unit'

    class TestConsolidation < Test::Unit::TestCase
    def test_should_remove_repeated_Xs_but_save_one_with_highest_Y
    initial = [ [ 1, 12 ], [ 3, 15 ], [ 3, 17 ], [ 3, 22 ], [ 7, 45 ] ]
    desired = [ [ 1, 12 ], [ 3, 22 ], [ 7, 45 ] ]
    assert_equal desired, initial.consolidate
    end
    end

    class Array
    def consolidate
    # Too embarrassed to show current hack.
    end
    end

    My least surprise inclination had me researching #uniq and #compact
    to see if they took a block like #sort.

    Thanks,
    --
    Bil Kleb
    http://fun3d.larc.nasa.gov
    Bil Kleb, Jan 7, 2008
    #1
    1. Advertising

  2. Bil Kleb

    Pit Capitain Guest

    2008/1/7, Bil Kleb <>:
    > I'm looking for an elegant way to do the following:
    > (...)


    Bil, this makes the test pass:

    class Array
    def consolidate
    Hash[*flatten].sort
    end
    end

    Regards,
    Pit
    Pit Capitain, Jan 7, 2008
    #2
    1. Advertising

  3. 2008/1/7, Bil Kleb <>:
    > Hi,
    >
    > I'm looking for an elegant way to do the following:
    >
    > Given an sorted array of (X,Y) pairs where Y is strictly
    > increasing, i.e., no repeated Ys.
    >
    > Compact pairs with repeated Xs, keeping the pair with
    > the highest Y value.
    >
    > E.g.,
    >
    > require 'test/unit'
    >
    > class TestConsolidation < Test::Unit::TestCase
    > def test_should_remove_repeated_Xs_but_save_one_with_highest_Y
    > initial = [ [ 1, 12 ], [ 3, 15 ], [ 3, 17 ], [ 3, 22 ], [ 7, 45 ] ]
    > desired = [ [ 1, 12 ], [ 3, 22 ], [ 7, 45 ] ]
    > assert_equal desired, initial.consolidate
    > end
    > end
    >
    > class Array
    > def consolidate
    > # Too embarrassed to show current hack.
    > end
    > end
    >
    > My least surprise inclination had me researching #uniq and #compact
    > to see if they took a block like #sort.


    This is easy with #inject:

    ar.inject([]) do |res, (x,y)|
    if res.empty? || res.last[0] != x
    res << [x,y]
    else
    res.last[1] = y
    end
    res
    end

    Cheers

    robert

    --
    use.inject do |as, often| as.you_can - without end
    Robert Klemme, Jan 7, 2008
    #3
  4. Pit Capitain wrote:
    > 2008/1/7, Bil Kleb <>:
    >> I'm looking for an elegant way to do the following:
    >> (...)

    >
    > Bil, this makes the test pass:
    >
    > class Array
    > def consolidate
    > Hash[*flatten].sort
    > end
    > end


    That certainly is elegant!

    Maybe Bil wanted to preserve the monotonicity?

    class Array
    def consolidate
    Hash[*flatten].sort_by {|x,y| y}
    end
    end

    p [ [5,1] , [3,2] ].consolidate # ==> [[5, 1], [3, 2]]

    --
    vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407
    Joel VanderWerf, Jan 7, 2008
    #4
  5. A more general solution:

    require 'set'

    class Array
    def uniq(&blk)
    blk ||= lambda {|x| x}
    already_seen = Set.new
    uniq_array = []

    self.each_with_index do |value, i|
    x = blk.call(value)
    unless already_seen.include? x
    already_seen << x
    uniq_array << value
    end
    end
    uniq_array
    end
    end

    and

    class Array
    def consolidate
    reverse.uniq {|a| a.first}.reverse
    end
    end

    Of course, the value that you keep for uniq is kinda arbitrary.

    Dan
    - Hide quoted text -

    On 1/7/08, Joel VanderWerf <> wrote:
    > Pit Capitain wrote:
    > > 2008/1/7, Bil Kleb <>:
    > >> I'm looking for an elegant way to do the following:
    > >> (...)

    > >
    > > Bil, this makes the test pass:
    > >
    > > class Array
    > > def consolidate
    > > Hash[*flatten].sort
    > > end
    > > end

    >
    > That certainly is elegant!
    >
    > Maybe Bil wanted to preserve the monotonicity?
    >
    > class Array
    > def consolidate
    > Hash[*flatten].sort_by {|x,y| y}
    > end
    > end
    >
    > p [ [5,1] , [3,2] ].consolidate # ==> [[5, 1], [3, 2]]
    >
    > --
    > vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407
    >
    >
    Daniel Finnie, Jan 7, 2008
    #5
  6. Bil Kleb

    Bil Guest

    On Jan 7, 10:45 am, Robert Klemme <> wrote:
    >
    > This is easy with #inject:


    Hey, thanks everyone for the help!

    I would have replied sooner, but my USENET feed dropped your replies
    and I only now thought to look at groups.google.com.

    Thanks again,
    --
    http://twitter.com/bil_kleb
    Bil, Jan 12, 2008
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. morrell
    Replies:
    1
    Views:
    949
    roy axenov
    Oct 10, 2006
  2. Replies:
    1
    Views:
    258
    Michele Dondi
    Jan 29, 2008
  3. Belorion

    Array::uniq { block } ?

    Belorion, Jan 26, 2005, in forum: Ruby
    Replies:
    22
    Views:
    276
    David A. Black
    Jan 31, 2005
  4. Edward Faulkner

    Block that takes a block

    Edward Faulkner, Sep 20, 2005, in forum: Ruby
    Replies:
    1
    Views:
    124
    Brian Mitchell
    Sep 20, 2005
  5. Lee Griffiths
    Replies:
    8
    Views:
    136
    Roger Pack
    Feb 13, 2011
Loading...

Share This Page