sorting on hash contents

M

Mark Probert

Hi, all.

Say I have a hash of 4 elements, val[1..3] and name

h["a"] = "1,2,3,fred"
h["b"] = "4,1,2,jack"
h["c"] = "3,3,4,jill"

How do I define:

def sort_hash(field="val1")
h.each { |key, data|
(val1, val2, val3, name) = data.split(/,/)
# etc..
end

elegantly?

One possibility is to write it out to a tempfile and use
the external `sort` command. Is there an easy pure-ruby
solution?

TIA,
 
A

ahoward

Hi, all.

Say I have a hash of 4 elements, val[1..3] and name

h["a"] = "1,2,3,fred"
h["b"] = "4,1,2,jack"
h["c"] = "3,3,4,jill"

How do I define:

def sort_hash(field="val1")
h.each { |key, data|
(val1, val2, val3, name) = data.split(/,/)
# etc..
end

elegantly?

One possibility is to write it out to a tempfile and use
the external `sort` command. Is there an easy pure-ruby
solution?

TIA,

whenever i end up needing sorted hashes i ALWAYS use the rbtree package from
the raa - it is awesome. eg:

----CUT----
#!/usr/bin/env ruby
require 'rbtree' # get this from the raa - it's FAST and always sorted

class DB < RBTree
attr :pks
alias pk pks
attr :fields
attr :n_pks
attr :n_fields
attr :indices
def initialize args
@pks = (args[:pks] or args[:pk]).to_a
@fields = args[:fields].to_a
pks.map{|k| raise unless fields.include? k}
@n_pks = pks.size
@n_fields = fields.size
@indices = {}
fields.map{|f| indices[f] = RBTree.new}
end
def []= k, v
k, v = [k, v].map{|a| a.to_a}
raise unless k.size == pk.size and v.size == fields.size
# map each field back to this k->v pair...
@fields.each_with_index{|f,i| indices[f][v] = [k,v]}
super k, v
end
def sorted_by field
raise unless block_given?
indices[field].map{|k,v| yield k, v}
end
end


db = DB.new :pk => 'f0', :fields => ['f0', 'f1', 'f2']

db['a'] = '0', 'C', 'b'
db['b'] = '1', 'B', 'a'
db['c'] = '2', 'A', 'c'

puts '--'
db.sorted_by('f0') do |k,v|
p v
end

puts '--'
db.sorted_by('f1') do |k,v|
p v
end

puts '--'
db.sorted_by('f2') do |k,v|
p v
end

__END__
OUTPUT:
--
[["a"], ["0", "C", "b"]]
[["b"], ["1", "B", "a"]]
[["c"], ["2", "A", "c"]]
--
[["c"], ["2", "A", "c"]]
[["b"], ["1", "B", "a"]]
[["a"], ["0", "C", "b"]]
--
[["b"], ["1", "B", "a"]]
[["a"], ["0", "C", "b"]]
[["c"], ["2", "A", "c"]]
----CUT----

-a
====================================
| Ara Howard
| NOAA Forecast Systems Laboratory
| Information and Technology Services
| Data Systems Group
| R/FST 325 Broadway
| Boulder, CO 80305-3328
| Email: (e-mail address removed)
| Phone: 303-497-7238
| Fax: 303-497-7259
| ~ > ruby -e 'p(%.\x2d\x29..intern)'
====================================
 
W

why the lucky stiff

I notice that rbtree works with Marshal, but not YAML. What methods
would have to be added to rbtree to fix this?

Currently, RBTree would need to inherit from Hash (or) introduce custom
is_complex_yaml?, to_yaml_type and to_yaml methods.

Here's custom methods which appear to work:

# Emitting the class
class RBTree
def to_yaml_type
"!ruby/rbtree"
end
def to_yaml( opts = {} )
YAML::quick_emit( self.object_id, opts ) { |out|
out.map( to_yaml_type ) { |map|
map.concat( self.to_a )
}
}
end
end

# Loading the class
YAML.add_ruby_type( 'rbtree' ) { |type, val|
r = RBTree.new
val.each { |k, v| r[k] = v }
r
}

# Test
a = RBTree['ducks', 100, 'bears', 12, 'badgers', 0]
puts a.to_yaml
p YAML::load( a.to_yaml )

# Looks like...
--- !ruby/rbtree
badgers: 0
bears: 12
ducks: 100

I'm not very happy with this technique though. I'd rather it was simpler,
such as:

class RBTree
include YamlHashMixin
end

_why
 

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
474,121
Messages
2,570,714
Members
47,282
Latest member
hopkins1988

Latest Threads

Top