Консолидация вложенных массивов и удаление консолидированных подмассивов?

Я пытаюсь взять кучу пар число-слово и сгруппировать слова по общим номерам. Я могу сопоставить числа, объединить подмассивы с общим номером и стереть первый из этих подмассивов. Но когда я пытаюсь удалить второе, я получаю эту ошибку:

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

Виновная линия - ary.delete_at(i+1) - была закомментирована. Вторичная проблема: MRBTree не принимает вложенные массивы в качестве входных данных...

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

выход:

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

Любая помощь приветствуется!

3 ответа

Решение

Ваша попытка не удалась, потому что вы модифицируете массив (который влияет на a.size) в петле. Условие окончания цикла не регулируется автоматически. Вы получаете доступ к вещам, которые вы удалили ранее.

Если ваш массив не слишком большой, это будет делать:

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

Это работает так:

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

Создание промежуточного массива и его передача обратно в хэш - это некоторые издержки. Если это окажется проблемой, что-то вроде этого может быть лучше:

h = Hash.new { | a, k | a[k] = [] }   # a hash with [] as default value
p ary.inject(h) { | a, (k, v) | a[k] << v; a }

Альтернативная версия для преобразования в хеш:

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

Похоже после

ary.delete_at(i)

размер массива уменьшается на единицу, следовательно i лучше, чем i+1:

# ary.delete_at(i+1)
ary.delete_at(i)
Другие вопросы по тегам