[Nuby] Sorting a Hash and keepint it as a Hash?

Another nuby question:

I have a Hash of objects which I want to sort by the values. Afterwards
I want to pull out the keys as one array and the values as another. So I
have code like so:

# sort by frequency ascending
@fault_sums = @fault_sums.sort {|a,b| a[1] <=> b[1]}
# Only keep top N
@fault_sums.slice!(0...-@number) if @number <= @fault_sums.size
    
fields = @fault_sums.keys
data = @fault_sums.values

But when I get to calling keys and values on the Hash I realized, sort
actually returns back a 2D array and those methods aren't defined on an
Array. Is there a way to make the 2D Array returned by the sort back
into a Hash?

Thanks,
Chris Williams

Well you could but you'd be back at square one since a hash is
unordered. An alternate method is to sort the keys by the values, ie

keys_ordered = some_hash.keys.sort { |a, b| some_hash[a] <=> some_hash[b] }

keys_ordered.each { |k|
       # do something with some_hash[k]
}

Of course you'll have to change it to fit exactly what you need.

···

On Tue, 14 Dec 2004 05:07:03 +0900, Williams, Chris <Chris.Williams@xeroxlabs.com> wrote:

Another nuby question:

I have a Hash of objects which I want to sort by the values. Afterwards
I want to pull out the keys as one array and the values as another. So I
have code like so:

# sort by frequency ascending
@fault_sums = @fault_sums.sort {|a,b| a[1] <=> b[1]}
# Only keep top N
@fault_sums.slice!(0...-@number) if @number <= @fault_sums.size

fields = @fault_sums.keys
data = @fault_sums.values

But when I get to calling keys and values on the Hash I realized, sort
actually returns back a 2D array and those methods aren't defined on an
Array. Is there a way to make the 2D Array returned by the sort back
into a Hash?

Thanks,
Chris Williams

"Williams, Chris" <Chris.Williams@xeroxlabs.com> schrieb im Newsbeitrag news:A0AC2703C633EB48B5DFEF66D6EBF7120E49DD@wcxlms02.wcrt.xeroxlabs.com...
Another nuby question:

I have a Hash of objects which I want to sort by the values. Afterwards
I want to pull out the keys as one array and the values as another. So I
have code like so:

You want sort with a block:

h={1=>"a",2=>"b"}

=> {1=>"a", 2=>"b"}

h.sort {|(k1,v1),(k2,v2)| v1 <=> v2}

=> [[1, "a"], [2, "b"]]

If you use inject you can get your two arrays in one go:

h.sort {|(k1,v1),(k2,v2)| v1 <=> v2}.inject([,]) {|(ks,vs),(k,v)| ks << k; vs << v; [ks,vs]}

=> [[1, 2], ["a", "b"]]

keys, values = h.sort {|(k1,v1),(k2,v2)| v1 <=> v2}.inject([,]) {|(ks,vs),(k,v)| ks << k; vs << v; [ks,vs]}

=> [[1, 2], ["a", "b"]]

keys

=> [1, 2]

values

=> ["a", "b"]

# sort by frequency ascending
@fault_sums = @fault_sums.sort {|a,b| a[1] <=> b[1]}
# Only keep top N
@fault_sums.slice!(0...-@number) if @number <= @fault_sums.size

fields = @fault_sums.keys
data = @fault_sums.values

But when I get to calling keys and values on the Hash I realized, sort
actually returns back a 2D array and those methods aren't defined on an
Array. Is there a way to make the 2D Array returned by the sort back
into a Hash?

This won't help as the hash you get then is unsorted again. If you need a copy, Hash#dup or Marshal.load(Marshal.dump(hash)) is a better choice.

Kind regards

    robert

Williams, Chris wrote:

I have a Hash of objects which I want to sort by the values. Afterwards
I want to pull out the keys as one array and the values as another. So I
have code like so:

# sort by frequency ascending
@fault_sums = @fault_sums.sort {|a,b| a[1] <=> b[1]}
# Only keep top N
@fault_sums.slice!(0...-@number) if @number <= @fault_sums.size
    fields = @fault_sums.keys
data = @fault_sums.values

But when I get to calling keys and values on the Hash I realized, sort
actually returns back a 2D array and those methods aren't defined on an
Array. Is there a way to make the 2D Array returned by the sort back
into a Hash?

There's no sorted Hashs in Standard Ruby so you will get a sorted Array of pairs.

However you can use a nifty trick to get the keys and values out of that:

[['key1', 'value1'], ['key2', 'value2']].transpose
# => [['key1', 'key2'], ['value1', 'value2']]

So this will solve your problem:

top_pairs = @faults_sum.sort_by { |key, value| -value }.first(@number)
fields, data = *top_pairs.transpose

Note that I chose to sort descending and to keep the first (high sum) entries instead of sorting ascending and to keep the lowest (high sum) entries in reverse.