compact or uniq that takes a block like sort?

B

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.

Thanks,
 
P

Pit Capitain

2008/1/7 said:
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
 
R

Robert Klemme

2008/1/7 said:
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
 
J

Joel VanderWerf

Pit said:
2008/1/7 said:
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]]
 
D

Daniel Finnie

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 -

Pit said:
2008/1/7 said:
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]]
 
B

Bil

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,
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top