#ruby
#ruby
Вопрос:
Я создаю массив сгруппированных индексов. Индексы — это точки внутри массива, которые соответствуют моим требованиям к группировке. Например, я группирую индексы из сетки, где объекты расположены горизонтально «близко» друг к другу. Это то, с чем я буду работать.
[[0,1,2],[3],[4,5],[5,6],[7,8],[8,9]]
Я хотел бы объединить по общим индексам. Таким образом, результат должен выглядеть следующим образом.
[[0,1,2],[3],[4,5,6],[7,8,9]]
Похоже, что это должно быть inject : для пар, если какие-либо внутренние элементы совпадают. Но я не вижу способа Ruby сделать это.
Комментарии:
1. Работает ли это транзитивно? Если есть
[1, 2]
,[2, 3]
,[3, 4]
, то что становится[1, 2, 3, 4]
?2. Неясно, что означает «точки внутри массива, которые соответствуют моим требованиям к группировке».
3. Нет. Мне нужно, чтобы индексы были разделены для группировок между сопоставлением индексов по горизонтали (элементы, которые близки друг к другу)… негруппированные остаются для последующего сопоставления по вертикали.
4. » вывод , с которым я должен работать» — это не вход , насколько это касается этой проблемы?
5. Насколько это возможно, да. Я обновил вопрос.
Ответ №1:
x.sort.inject([]) do |y, new|
(((y.last || []) amp; new).length > 0) ? y[0..-2].push(y.last | new) : y.push(new)
end.map(amp;:sort)
Комментарии:
1. Это работает отлично, пока мой порядок является последовательным. Добавление
.shuffle
в массив нарушает это. Это хороший ответ.2. Это правильный ответ.
x.sort.inject([]) { |y, new| ((y.last || []) amp; new).length > 0 ? y[0..-2].push(y.last | new) : y.push(new) }.map{|i|i.sort}
Пожалуйста, обновите свой код, чтобы он соответствовал этому, и я приму ваш ответ как правильный.3. Как вы пожелаете! Я рад, что мы нашли функциональное решение. Возможно, вы захотите изменить свой пример, чтобы сделать требования к сортировке понятными. Например, вы можете захотеть начать с
x.map(amp;:sort).sort
вместо простоx.sort
.
Ответ №2:
Зная Ruby, вероятно, есть более краткий способ сделать это, но это должно дать вам то, что вы хотите:
foo = [[0,1,2],[3],[4,5],[5,6],[7,8],[8,9]]
foo.inject([]) {|result,element|
if (result and existing = result.find_index{|a| !(element amp; [*a]).empty?})
tmp = result[existing]
result.delete_at(existing)
result << (tmp | element).sort
else
result << element
end
}.sort
Вывод:
=> [[0, 1, 2], [3], [4, 5, 6], [7, 8, 9]]
Логические:
Для каждого из них element
в исходном массиве проверьте недавно созданный array-so-far ( result
) на наличие любой записи, которая содержит любое из тех же чисел, что и следующий элемент, используя array intersection — !(element amp; [*a]).empty?
…
- если найдено, удалите указанную запись из
result
, объедините ее с новойelement
из исходного массива —(tmp | element)
— затем добавьте ее обратно вresult
- если не найдено, просто объедините
element
из исходного массива вresult
Комментарии:
1. Примечание: Небольшое редактирование, внесенное в исходный ответ, в частности,
find_index
логика теперь обрабатывает ввод не по порядку, например[[0,1,2],[3],[5,6],[7,8],[4,5],[8,9]]
.2. ДА. Это работает хорошо. Мне нужно будет добавить сортировку во внутренние массивы, поскольку порядок важен. Я протестировал это, включив shuffle в foo перед вводом
foo.shuffle.inject
, и он действительно присоединяется к правильным.3. Разве присвоение в операторе if не считается запретным? (Поскольку это чаще всего неправильно читается.)
4. Хороший вызов re: sort — я обновил ответ, чтобы включить и это, чтобы избежать потери его в комментариях. Что касается назначения в операторе if, я не уверен — это чрезвычайно полезный шаблон, который я видел, чтобы проверять существование вещи и одним махом преобразовывать ее в переменную, а не
tmp = result.find_index(...); if tmp ...
Ответ №3:
Кто-то может найти более компактный метод, но это работает…
array = [[0,1,2],[3],[4,5],[5,6],[7,8],[8,9]]
(0...array.length).each do |a|
(a 1...array.length).each do |b|
unless array[a].to_a amp; array[b].to_a == []
array[a].push(array[b]).flatten!.uniq!.sort!
array.delete_at(b)
b -= 1
end
end
end
p array
=> [[0, 1, 2], [3], [4, 5, 6], [7, 8, 9]]
Комментарии:
1. Это работает даже при перетасованном состоянии массива. Хорошая работа. Внутренние массивы не отсортированы.
Ответ №4:
Решения пока кажутся мне чрезмерно сложными. Я предлагаю это (предполагая, что каждый элемент arr
непустой и содержит только целые числа):
arr = [[0, 1, 2], [3],
[4, 5], [5, 6],
[7, 8, 9], [9, 10], [10, 11],
[12, 13], [13], [13, 14]]
arr.each_with_object([]) do |a,b|
if b.any? amp;amp; b.last.last == a.first
b[-1] = a[1..-1]
else
b << a
end
end
#=> [[0, 1, 2], [3], [4, 5, 6], [7, 8, 9, 10, 11], [12, 13, 14]]
В качестве альтернативы вы могли бы сделать это, выполнив переход arr
с помощью перечислителя:
enum = arr.each
b = [enum.next]
loop do
a = enum.next
if b.last.last == a.first
b[-1] = a[1..-1]
else
b << a
end
end
b