Самый чистый способ перебрать главный набор, обрабатывая каждый поднабор, в Ruby - PullRequest
1 голос
/ 13 февраля 2011

Это распространенный сценарий, и я никогда не доволен решениями. У вас есть набор данных, в данном случае просто предположим, что строки из БД упорядочены по категориям.

Вы хотите создать буфер с подмножеством каждой категории, и при каждом изменении категории выполнить некоторую обработку, а затем очистить буфер. Предположим, что process () обернут в сложную логику, которую вы не хотите дублировать

У кого-нибудь есть лучшие настройки?

# category, city
data = [
          [10, 'citya'],
          [10, 'cityb'],
          [11, 'citya'],
          [11, 'cityb'],
          [11, 'citya'],
          [12, 'cityb'],
          [12, 'cityg']
       ]

# do some heavy lifting in here
def process(buf) p buf; end

cur_cat = nil
cur_cat_buf = []
data.each do |r|
  if r[0] != cur_cat
    cur_cat = r[0]
    process(cur_cat_buf) #<-- assume this is conditional, complex
    cur_cat_buf.clear
  end
  cur_cat_buf << r[1]
end
process(cur_cat_buf) #<-- assume the conditional is duplicated...ack.

Это другая техника, и она просто ужасна. Грязно, ужасно! Всегда заглядывая в будущее, проверяя, является ли он нулевым или другим и т. Д. Тьфу ...

cur_cat = data[0][0] if data.length > 0
cur_cat_buf = []
data.each_with_index do |r, i|
  cur_cat_buf << r[1]

  # look ahead
  if data[i+1] == nil or data[i+1][0] != cur_cat
    cur_cat = data[i+1][0] if data[i+1] != nil
    process(cur_cat_buf)
    cur_cat_buf.clear
  end
end

Это еще одна альтернатива. Конечно, лучше, чем предыдущий.

cur_cat = nil
cur_cat_buf = []
for i in 0..(data.length)
  if (r = data[i]) == nil or r[0] != cur_cat
    process(cur_cat_buf)
    break unless r

    cur_cat_buf.clear
    cur_cat = r[0]
  end

  cur_cat_buf << r[1]
end

Я хочу чистое, элегантное решение. Должен быть лучший способ!

Ответы [ 2 ]

2 голосов
/ 13 февраля 2011
data.group_by(&:first).each_value {|buffer| process(buffer.map &:last) }
1 голос
/ 13 февраля 2011
data.group_by(&:first).each_value do |pairs| 
  process(pairs.map(&:last)) 
end

Или эквивалент, но немного более многословный, но немного более явный:

data.group_by { |category_id, city| category_id }.each_value do |pairs| 
  process(pairs.map { |category_id, cities| cities }) 
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...