1

I'm trying to take a bunch of number-word pairs and group the words according to common numbers. I can match the numbers, merge the subarrays that share the number, and erase the first of those subarrays. But when I try to delete the second, I get this error:

"in block in <main>': undefined method[]' for nil:NilClass (NoMethodError)"

The guilty line -- ary.delete_at(i+1) -- has been commented out. Secondary problem: MRBTree is not taking the nested arrays as input...

ary = [[2.28, "cat"], [2.28, "bat"], [2.327, "bear"], [2.68, "ant"], [2.68, "anu"]]
i = 0

for i in 0 ... ary.size - 1
    if ary[i][0] == ary[i+1][0]
        b = (ary[i]+ary[i+1]).uniq
        ary.delete_at(i)
                # ary.delete_at(i+1)
        c = [b.first], b.pop(b.length - 1)
        h = Hash[*c]
        ary.push(*h)
        # mrbtree = MultiRBTree[c]
    end
end

puts ary.inspect

output:

# => [
# =>   [2.28, "bat"], 
# =>   [2.327, "bear"], 
# =>   [2.68, "anu"], 
# =>   [
# =>     [2.28], ["cat", "bat"]
# =>   ], 
# =>   [
# =>     [2.68], ["ant", "anu"]
# =>   ]
# => ]

Any help appreciated!

Phrogz
  • 296,393
  • 112
  • 651
  • 745
racknuf
  • 444
  • 3
  • 12

3 Answers3

2

Your attempt is failing because you are modifying the array (which has impact on a.size) in the loop. The loop end condition is not adjusted automagically. You are accessing things you have deleted before.

If your array is not too big, this will do:

p Hash[ary.group_by(&:first).map { | k, v | [k, v.map(&:last)] }]
# => {2.28=>["cat", "bat"], 2.327=>["bear"], 2.68=>["ant", "anu"]}

It works this way:

ary.group_by(&:first)   # group the 2-elem arrays by the number, creating a hash 
                        #     like {2.28=>[[2.28, "cat"], [2.28, "bat"]], ...}
.map  { | k, v | ... }  # change the array of key-value pairs to
[k, v.map(&:last)]      # pairs where the value-array contains just the strings
Hash[ ... ]             # make the whole thing a hash again

Creating an intermediate array and transferring it back to a hash is some overhead. If this turns out to be an issue, something like this might be better:

h = Hash.new { | a, k | a[k] = [] }   # a hash with [] as default value
p ary.inject(h) { | a, (k, v) | a[k] << v; a }
undur_gongor
  • 15,657
  • 5
  • 63
  • 75
0

It looks like after

ary.delete_at(i)

the size of array is decreased by one, hence i is better than i+1:

# ary.delete_at(i+1)
ary.delete_at(i)
Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
0

Alternate version for converting to hash:

ary = [[2.28, "cat"], [2.28, "bat"], [2.327, "bear"], [2.68, "ant"], [2.68, "anu"]]
hsh = {}

ary.each {|pair| hsh[pair[0]].nil? ? hsh[pair[0]] = [pair[1]] : hsh[pair[0]] << pair[1]}

puts hsh.inspect  # => {2.28 => ["cat", "bat"], 2.327 => ["bear"], 2.68 => ["ant", "anu"]}
tigeravatar
  • 26,199
  • 5
  • 30
  • 38