Есть ли способ разделить массив, как это?

[1, 2, 3, 4, 5, 6, 7, 8, 9].split(3, 4, 2)
#=> [[1, 2, 3],[4, 5, 6, 7],[8, 9]]
4
Pistorius 31 Авг 2017 в 16:21

5 ответов

Лучший ответ

Нет, нет, но вы можете легко написать один самостоятельно.

class Array
  def in_groups_of_n(*sizes)
   sizes.map(&method(:shift))
  end
end

< EM> Пример :

arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
arr.in_groups_of_n(3, 4, 2)
# => [[1, 2, 3], [4, 5, 6, 7], [8, 9]]

демонстрация

Если вам нужна неразрушающая версия, вы можете использовать метод dup:

class Array
  def in_groups_of_n(*sizes)
   duplicate = dup
   sizes.map { |size| duplicate.shift(size) }
  end
end

arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
arr.in_groups_of_n(3,4,2)
# => [[1, 2, 3], [4, 5, 6, 7], [8, 9]
arr
# => [1, 2, 3, 4, 5, 6, 7, 8, 9]

демонстрация

1
potashin 31 Авг 2017 в 14:15

Вот наивная реализация Array:

class Array
  def multi_split(*sizes)
    r = []
    e = self.each
    sizes.each do |size|
      t = []
      size.times do
        t << e.next
      end
      r << t
    end
    r
  end
end

p [1, 2, 3, 4, 5, 6, 7, 8, 9].multi_split(3, 4, 2)
# [[1, 2, 3], [4, 5, 6, 7], [8, 9]]

@Stefan упомянул, что может иметь смысл реализовать это на Enumerable:

module Enumerable
  def multi_split(*sizes)
    Enumerator.new do |yielder|
      e = self.each
      sizes.each do |size|
        yielder << Array.new(size){ e.next }
      end
    end
  end
end

p [1, 2, 3, 4, 5, 6, 7, 8, 9].multi_split(3, 4, 2).to_a
# [[1, 2, 3], [4, 5, 6, 7], [8, 9]]
1
Eric Duminil 31 Авг 2017 в 14:31

Другой вариант (без потерь, если разбиения не равны размеру массива

def split_at(arr,splits)
  rest = arr.last(arr.size - splits.reduce(:+))
  enum = arr.to_enum
  splits.map do |n| 
    n.times.map { enum.next } 
  end.concat(rest.empty? ? [] : [rest])
end

Затем называется как

split_at (1..9), [3,4,2]
#=> [[1, 2, 3], [4, 5, 6, 7], [8, 9]]
split_at (1..22), [3,4,2]
#=> [[1, 2, 3], [4, 5, 6, 7], [8, 9], [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22]]

Примере

1
engineersmnky 31 Авг 2017 в 15:17
class Array
  def split_by_number(*sizes)
    sizes.each_with_object([]) { |n,a| a << [a.empty? ? 0 : a.last.sum, n] }.
          map { |start, nbr| self[start, nbr] }
  end
end

[1, 2, 3, 4, 5, 6, 7, 8, 9].split_by_number 3, 4, 2
  #=> [[1, 2, 3], [4, 5, 6, 7], [8, 9]]

Обратите внимание, что

[3, 4, 2].each_with_object([]) { |n,a| a << [a.empty? ? 0 : a.last.sum, n] }
  #=> [[0, 3], [3, 4], [7, 2]]
1
Cary Swoveland 1 Сен 2017 в 18:21

Неизменная версия с λ:

▶ splitter = ->(array, *parts) do 
    parts.reduce([[], 0]) do |acc, i|
      right = acc.last + i
      [acc.first << (acc.last...right), right]
    end.first.map { |r| array[r] }
  end
#⇒ #<Proc:0x0055ae3d9ae7c8@(pry):18 (lambda)>
▶ splitter.((1..9).to_a, 3, 4, 2)
#⇒ [[1, 2, 3], [4, 5, 6, 7], [8, 9]]
2
Aleksei Matiushkin 31 Авг 2017 в 13:54