Compact or uniq that takes a block like sort?

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, this makes the test pass:

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

Regards,
Pit

···

2008/1/7, Bil Kleb <Bil.Kleb@nasa.gov>:

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

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

···

2008/1/7, Bil Kleb <Bil.Kleb@nasa.gov>:

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.

--
use.inject do |as, often| as.you_can - without end

Pit Capitain wrote:

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]]

···

2008/1/7, Bil Kleb <Bil.Kleb@nasa.gov>:

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

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,

···

On Jan 7, 10:45 am, Robert Klemme <shortcut...@googlemail.com> wrote:

This is easy with #inject:

--
http://twitter.com/bil_kleb

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 <vjoel@path.berkeley.edu> wrote:

Pit Capitain wrote:
> 2008/1/7, Bil Kleb <Bil.Kleb@nasa.gov>:
>> 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