Работа с массивами Ruby на основе свойств индекса - PullRequest
0 голосов
/ 17 сентября 2009

У меня есть массив в Ruby. Я хочу:

  1. Получить подмножество элементов на основе их положения в массиве - скажем, каждый 5-й элемент. Я могу сделать это с each_index, или расширить и создать метод select_with_index.

  2. Выполните некоторую операцию над подмножеством, которое зависит от всего подмножества - скажем, subset.map {| element | subset.sum - element}

  3. Это бит, на котором я застрял: создайте новый массив с правильными элементами, замененными элементами на шаге 2. Например:

Так что мой очень запутанный пример может иметь:

Start:   [3,0,6,11,77,2,1,5,48,9,122,0,43,13,564]

Select:  [3,2,122]

Map:     [124,125,5]

Replace: [124,0,6,11,77,125,1,5,48,9,5,0,43,13,564]

Как я могу выполнить замену элегантным способом? Есть ли способ создать метод, который бы объединял два массива и брал блок {| i | я% 5 == 0}?

(Это мотивировано подходом к написанию компактного решателя судоку, чтобы выучить немного Ruby ...)

РЕДАКТИРОВАТЬ: были изменены значения примера. Надеюсь, теперь это стало понятнее.

Ответы [ 4 ]

2 голосов
/ 17 сентября 2009
a = [3, 0, 6, 11, 77, 2, 1, 5, 48, 9, 122, 0, 43, 13, 564]

# per your requirements
def replace_indices(ary, &index_selector)
  indices = ary.each_index.select(&index_selector)
  sum = indices.inject(0) {|sum, i| sum += ary[i]}
  indices.each {|i| ary[i] = sum - ary[i]}
  ary
end

p new = replace_indices(a.dup) {|i| i % 5 == 0}

# just pass the "index step value"
def replace_each_step(ary, step)
  sum = 0
  ary.each_index .
      select {|i| i % step == 0} .
      collect {|i| sum += ary[i]; ary[i]} .
      each_with_index {|e,i| ary[i*step] = sum - e}
  ary
end

p new = replace_each_step(a.dup, 5)
1 голос
/ 18 сентября 2009

Я бы, наверное, просто решил это с помощью Enumerable#enum_for(:each_with_index)

require 'enumerator'

values = [3,0,6,11,77,2,1,5,48,9,122,0,43,13,564]

subset_with_indexes = values.enum_for(:each_with_index).select { |v,i| i % 5 == 0 }
#=> [ [3,0], [2,5], [122,10] ]

subset_sum = subset_with_indexes.inject(0) { |s,(v,i)| s+v }
#=> 127

subset_with_indexes.each do |v,i|
  values[i] = subset_sum - v
end

values #=> [124, 0, 6, 11, 77, 125, 1, 5, 48, 9, 5, 0, 43, 13, 564]

Или

require 'enumerator'

values = [3,0,6,11,77,2,1,5,48,9,122,0,43,13,564]
values_with_indexes = values.enum_for(:each_with_index)

subset_sum = values_with_indexes.inject do |s,(v,i)| 
  i % 5 == 0 ? s + v : s
end #=> 127

new_values = values_with_indexes.map do |v,i|
  i % 5 == 0 ? subset_sum - v : v
end  #=> [124, 0, 6, 11, 77, 125, 1, 5, 48, 9, 5, 0, 43, 13, 564]
1 голос
/ 17 сентября 2009

Предполагая, что метод sum является тем же, что и в Rails, он может сработать:

a = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
b = []
a.each_index {|i| b[i] = a[i] if i%5 == 0}
c = b.map{|p| p.nil? ? nil : b.sum{|i| i.nil? ? 0 : i} - p}
c.each_index {|i| a[i] = c[i] unless c[i].nil?}

Я оставляю на ваше усмотрение преобразовать его во что-то полезное :) По сути, теория заключается в том, чтобы сохранить все индексыв исходном массиве даже в подмножестве.Таким образом, легко узнать, какие из них заменить позже.Вы также можете использовать Hash для этого, если есть более сложные вычисления.

Вот немного более компактная версия этого:

a = [3,0,6,11,77,2,1,5,48,9,122,0,43,13,564]
b = []

a.each_index {|i| b[i] = a[i] if i%5 == 0}
b.each_with_index {|obj, i| 
    a[i] = b.inject(0){|m,v| v.nil? ? m : v} - obj unless obj.nil?}
0 голосов
/ 17 сентября 2009

Вы можете сделать это за один проход, если вам не нужно знать длину подмножества.

a = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
p a.map {|i| i % 5 == 0 ? "foo" : i }
# => ["foo", 1, 2, 3, 4, "foo", 6, 7, 8, 9, "foo", 11, 12, 13, 14, "foo"]

Если у вас есть Array#sum, реализованный в другом месте, вы можете сделать это в два прохода следующим образом:

a = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
sum = a.select {|i| i % 5 == 0 }.sum
p a.map {|i| i % 5 == 0 ? sum - i : i }
# => [30, 1, 2, 3, 4, 25, 6, 7, 8, 9, 20, 11, 12, 13, 14, 15]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...