Ruby: Dividir um array em arrays menores

Palavras-chave: Ruby, array, divide

Suponha que você tenha um array de X elementos e precise dividi-lo em arrays com Y elementos cada. A flexibilidade de Ruby nos permite definir operadores novos para as classes, então vamos criar um operador de dividir (/) para a classe Array:

class Array
   def / len
     inject([]) do |ary, x|
       ary << [] if [*ary.last].nitems % len == 0
       ary.last << x
       ary
     end
   end
end

Vamos rodar agora com alguns arrays para ver o que aconteceu:

p (1..10).to_a / 2
=> [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]
p %w(hiro ando nikki jessica sylar) / 2
=> [["hiro", "ando"], ["nikki", "jessica"], ["sylar"]]
p (1..10).to_a / 4
=> [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]]

Vamos analisar cada caso:

  1. Aqui temos um array de 10 elementos e dividimos por 2, retornando vários arrays com 2 elementos.
  2. Aqui temos um array ímpar de 5 elementos que dividimos por 2, retornando dois arrays com 2 elementos e um último com apenas um elemento (deixa aquele cara longe!).
  3. Aqui temos um array de 10 elementos que é dividido por 4, retornando dois arrays com 4 elementos e um último array com 2 elementos.

Ou seja, sempre que o resultado da divisão não for exata, temos o resto no último elemento retornado. As expressões que usei acima do tipo (1..10).to_a nada mais fazem do que converter uma Range em um Array.

O código do método “/” é meio críptico, mas se eu tentar explicar aqui vai ficar meio comprido e vou levar uma bronca, então dêem uma queimada nos neurônios aí. ;-)

This entry was posted in Ruby. Bookmark the permalink.

8 Responses to Ruby: Dividir um array em arrays menores

  1. shairon says:

    Afi! Elegantíssimo!! So não gostei do teu array :( cadê a Claire e o Peter Petrelli ??

  2. shairon says:

    require ‘enumerator’

    (1..10).to_a.enum_cons(2).to_a

    => [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10]]

  3. TaQ says:

    Shairon,

    Fica um pouquinho diferente, pois o último elemento do subarray é o primeiro elemento do próximo subarray (como no seu próprio exemplo), olha só aplicado no meu exemplo:

    %w(hiro ando nikki jessica sylar).enum_cons(2).to_a
    => [[“hiro”, “ando”], [“ando”, “nikki”], [“nikki”, “jessica”], [“jessica”, “sylar”]]

    A não ser que alguém ali tenha o poder de duplicar o outro não daria certo. E se você souber de algum spoiler que tenha alguém assim não conta (e olha, só sobraram o Hiro e o Sylar que não foram duplicados) !!! :-)

  4. TaQ says:

    Só para complementar: o Rails insere um método chamado in_groups_of que nos permite fazer algo do tipo:

    %w(hiro ando nikki jessica sylar).in_groups_of(2)
    => [[“hiro”, “ando”], [“nikki”, “jessica”], [“sylar”, nil]]
    >> (1..10).to_a.in_groups_of(4)
    => [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, nil, nil]]

    Uma pequena diferença ali é que ele completa o array com nil, *se não for passado um segundo parâmetro que especifica como serão completados os elementos faltantes*. Para ter um comportamento similar ao original, seria:

    >> %w(hiro ando nikki jessica sylar).in_groups_of(2,false)
    => [[“hiro”, “ando”], [“nikki”, “jessica”], [“sylar”]]
    >> %w(hiro ando nikki jessica sylar).in_groups_of(2,false)
    => [[“hiro”, “ando”], [“nikki”, “jessica”], [“sylar”]]

    Dá para usar outros preenchimentos também:

    >> %w(hiro ando nikki jessica sylar).in_groups_of(2,”who?”)
    => [[“hiro”, “ando”], [“nikki”, “jessica”], [“sylar”, “who?”]]
    >> (1..10).to_a.in_groups_of(4,0)
    => [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 0, 0]]

  5. shairon says:

    Ops um erro meu

    (1..10).to_a.enum_slice(2).to_a
    => [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]

  6. TaQ says:

    Uia, agora ficou melhor de bom! Muito legal!
    Menor que isso não dá! :-)

    >> require “enumerator”
    => true
    >> %w(hiro ando nikki jessica sylar).to_a.enum_slice(2).to_a
    => [[“hiro”, “ando”], [“nikki”, “jessica”], [“sylar”]]

  7. Ivan says:

    Acho que até dá TaQ ;D~

    Tem só uma redundância nos códigos… testei isso no irb:

    >> require ‘enumerator’
    => true
    >> %w(hiro ando nikki jessica sylar).enum_slice(2).to_a
    => [[“hiro”, “ando”], [“nikki”, “jessica”], [“sylar”]]
    >> (1..10).enum_slice(2).to_a
    => [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]

    espero que esteja certo ;]

    abração!

  8. Ivan says:

    E testando um pouco mais… podemos fazer assim:

    >> require ‘enumerator’
    => true
    >> class Array
    >> def / len
    >> enum_slice(len).to_a
    >> end
    >> end
    => nil
    >> class Range
    >> def / len
    >> enum_slice(len).to_a
    >> end
    >> end
    => nil
    >> %w(hiro ando nikki jessica sylar) / 2
    => [[“hiro”, “ando”], [“nikki”, “jessica”], [“sylar”]]
    >> (1..10) / 2
    => [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]

    ;]

Comments are closed.