Arrays en Ruby

Publicado el Miércoles, 21 de septiembre de 2011
Ruby

Ruby

Hoy voy a compartir mis apuntes sobre Arrays en Ruby. Como comentaba en posts anteriores, vengo estudiando con el libro Programming Ruby 1.9: The Pragmatic Programmers' Guide. Vengo acompañando la lectura con los Ruby Koans, y algunos video tutoriales. Ya repasé lo de Rails For Zombies, y descubrí otro excelente recurso que son los RailsCasts. Vamos a ver si en algún momento tenemos algo escrito en Ruby para compartir 🙂

Vamos entonces con estructuras de datos de Ruby. Los Arrays y Hashes son dos clases creadas para el manejo de colecciones:

La maestría en estas dos clases es clave para ser un programador Ruby efectivo. Esta maestría puede llevar tiempo, porque ambas clases tienen interfaces muy grandes.

Teniendo en cuenta esta cita del libro, veamos una definición bien específica de qué es un Array en Ruby:

Arrays
Colección de referencias a objetos.

En Ruby, las variables guardan la referencia a un objeto, y no el objeto en sí. Cada referencia a un objeto ocupa una posición en el array identificado por un índice entero.

a = [3.141592, "pi", 42]
a.class		# Array
a.length	# 3
a[0]		# 3.141592
a[2]		# 42
a[3]		# nil
a[15]		# nil
 
b = Array.new
b.class		# Array
b.length	# 0
b[0] = "Uno"
b[1] = "Dos"
b		# ["Uno", "Dos"]

Se indexan los elementos usando el operador [], un método de instancia de la clase Array que puede ser sobrescrito. Llamar a una posición de un entero positivo, retorna el objeto en ese lugar o nil si no hay nada ahí. Con los índices negativos, se cuenta desde el final siendo -1 el último elemento del array, y -array.length el primero.

a = [1,  5,  6, 38, 95]
#    0,  1,  2,  3,  4	índices positivos
#   -5, -4, -3, -2, -1	índices negativos
 
a[-1] 		# 95
a[-3] 		# 6
a[-56]		# nil

Se pueden indexar con un par de números (inicio, cantidad). Esto devuelve un nuevo array con referencias a la cantidad de objetos comenzando en la posición inicio.

a = [1, 5, 6, 38, 95]
a[1,3]		# [5, 6, 38]
a[-3, 2]	# [6, 38]

También se pueden usar rangos. Ruby implementa un objeto para representar rangos como los meses de Enero a Diciembre, secuencias de números y demás. En el caso de los arrays, se usan los rangos que implementan secuencias (también implementan condiciones e intervalos). Estos funcionan así:

1..10
1...10

Cuando se usan los tres puntos, se excluye al último elemento del rango.

De esta manera, podemos acceder a los elementos de un array así:

a = [1, 5, 6, 38, 95]
a[1..3]		# [5, 6, 38]
a[-4..4]	# [5, 6, 38, 95]

El operador [] tiene un operador []= para setear elementos en el array. Si se usa un solo índice entero, el elemento en esa posición se reemplaza con lo que haya a la derecha de la asignación. Si quedan espacios sin asignar en el medio, estos se llenan con nil.

a = [1, 5, 6, 38, 95]
a[1] = 'Stegosaurus'	#[1, 'Stegosaurus', 6, 38, 95]
a[3] = [5, 6]		#[1, 'Stegosaurus', 6, [5, 6], 95]
a[6] = 99		#[1, 'Stegosaurus', 6, [5, 6], 95. nil, 99]

Si el índice de la asignación []= son dos números(un inicio y longitud) o un rango, los dos elementos en el array original son reemplazados por lo que haya a la derecha de la asignación.

a = [1, 5, 6, 38, 95]
a[2,2] = 'Velociraptor'	#[1, 5, 'Velociraptor', 95]
#Si la longitud es 0, se inserta el valor de la derecha antes de la posición de inicio
a[2,0] = 'Nintendo 64'	#[1, 5, 'Nintendo 64', 'Velociraptor', 95]
#Si el valor a asignar es un array, se usan sus elementos en la asignación
a[1,1] = [4, 7, 82]	#[1, 4, 7, 82, 'Nintendo 64', 'Velociraptor', 95]
a[0..3] = []		#['Nintendo 64', 'Velociraptor', 95]
a[5..6] = 'AYBABTU', 98	#['Nintendo 64', 'Velociraptor', 95, nil, nil, 'AYBABTU',  98]

Arrays como pilas y colas

Los arrays implementan las funciones push y pop que agregan y quitan elementos al final del array, por lo que pueden ser usados como pilas (stack):

pila = []
pila.push "Leonardo"
pila.push "Donatello"
pila.push "Raphael"
pila.push "Michelangelo"
 
puts pila.pop	# Michelangelo
puts pila.pop	# Raphael
p pila		# ["Leonardo", "Donatello"]

Los métodos shift y unshift agregan y quitan elementos del principio del Array. Combinando ambos obtenemos una cola FIFO:

cola = []
cola.push "Leonardo"
cola.push "Donatello"
puts cola.shift
puts cola.shift

En este código, los elementos se quitan del array en el orden que fueron insertados. Está bueno ir escribiendo todo esto en el shell interactivo de Ruby (irb), el cual debería estar instalado en su sistema si instalaron Ruby. De esa forma pueden ir comprobando los resultados esperados de cada método y demás.

Otra opción es usar los métodos first y last. Éstos devuelven los elementos pero sin quitarlos del array. Se puede pasar como parámetro la cantidad de elementos que queremos recibir.

Más info:

17 comentarios en este post

Feed de comentarios
  1. Avatar

    Deleteman 21 septiembre. 2011 - 11:16

    Muy bueno el articulo, cubre la base de los arrays muy bien!

    Un pique sobre arrays (que va de la mano con la asignación en paralelo) es que podés separar un array en head y tail (“ala” programación funcional) bien fácil, haciendo (por lo menos en ruby 1.9.2):

    head, *tail = [1,2,3,4,5]

    Te queda head = 1 y tail = [2,3,4,5], así podés hacer cosas como:

    def sum list
      head, *tail = list
      s = (tail.count == 0)?head:(head + sum(tail))
    end

    Y ahí podés llamar a, por ejemplo: sum (1..1000).to_a y te hace la suma super rápido.

    Da para chiviar un poco con recursión 🙂

    • Avatar

      Fernando 22 septiembre. 2011 - 10:27

      ¡Gracias por el comentario! Modifiqué un poco el comentario para darle formato (acá hay más info de cómo hacerlo).

      Voy a jugar un rato con el código que aportaste, ¡gracias por el pique! Es el tipo de aportes que busco con estos posts 🙂

    • Avatar

      Fernando 28 septiembre. 2011 - 19:56

      ¡Gracias!

      No he tocado Android en un buen tiempo, pero no estaría mal armar algo. Tengo ganas de probar algo en Android. Por ahora vengo estudiando intensivamente Ruby, pero ni bien meta mano en Android nuevamente, ¡lo publico!

      Saludos

  2. Avatar

    Fabian Astorgan 24 diciembre. 2012 - 12:37

    Hola, estoy empezando en ruby..te hago una consulta a ver sime podes dar una mano.
    Necesito leer un archivo de texto y asignar cada linea a una variable distinta. Tengo una forma de resolver, pero es poco ortodoxa y ya que estoy aprendiendo quiero hacerlo bien.. mi idea (la que no funciona) seria asi:
    el con tenido del archivo supongamos:

    JUAN
    PEDRO
    PABLO

    File.read(archivo).each_line { | line | #leo el archivo
    vector = [“#{archive}”,”#{archive}”,”#{archive}”]# esta es la parte que no se.. la idea seria asignar cada linea en una matriz

    @variable1 = vector[0] # juan
    @variable2 = vector[1] # pedro

    No se si estoy mezclando peras con pepinos.. si mepodes dar una mano te lo agradesco.. Saludos

    • Avatar

      Fernando 25 mayo. 2013 - 06:25

      Fabian,
      Si entendí bien la pregunta, la cosa sería así:

      fernando@endor ~/ $ pry
      [1] pry(main)> vector = []
      []
      [2] pry(main)> File.read("./archivo").each_line do |line|
      [2] pry(main)*   vector << line #Asi agregas la línea al array
      [2] pry(main)* end  
      "JUANnPEDROnPABLOn"
      [3] pry(main)> puts vector.inspect
      ["JUANn", "PEDROn", "PABLOn"]
      nil
      [4] pry(main)> puts vector[0]
      JUAN
      nil
      [5] pry(main)> puts vector[1]
      PEDRO
      nil
      [6] pry(main)>

  3. Avatar

    Matias 21 julio. 2015 - 15:30

    como agregar varios datos en una matriz despues de un enter, se entiende??

    EJ: Yo quiero agregar una palabra, y al rato quiero agregar otra palabra y despues se me ocurre agregar otra palabra…. Esto tendria que ser en lineas diferentes y interactuando con el programa.

    En teoria los datos que uno agrega tiene que estar guardados en la matriz y si yo llamo a esa matriz tiene que devolver los datos que se guardaron!!!

    • Avatar

      Fernando 21 julio. 2015 - 19:08

      Hola Matías,
      Para agregar elementos a una matriz hay que usar el operador << o push. Ejemplo:

      2.2.2 :001 > matriz = []
       => [] 
      2.2.2 :002 > matriz << "Palabra"
       => ["Palabra"] 
      2.2.2 :003 > matriz << "Otra Palabra"
       => ["Palabra", "Otra Palabra"] 
      2.2.2 :004 > matriz.push "Tercera"
       => ["Palabra", "Otra Palabra", "Tercera"] 
      2.2.2 :005 > matriz
       => ["Palabra", "Otra Palabra", "Tercera"]

      Capaz que debería agregarlo al post si no quedó muy claro. ¡Saludos!

    • Avatar

      Fernando 28 abril. 2016 - 13:43

      Hola Ale,
      Para eliminar el elemento de una posición se puede usar el método delete_at:

      a = [1, 2, 3, 4, 5]
       => [1, 2, 3, 4, 5] 
      a.delete_at(3)
       => 4 
      a
       => [1, 2, 3, 5]

  4. Avatar

    Cielo 26 septiembre. 2017 - 15:41

    def medicinaMasVendida(tipos, ventas)
    end

    medicinaMasVendida([“amex”,”esitol”,”jaditor”,”rubicol”,”zainex”], [1000,1200,600,400,500])

    si tengo este array como hago que me regrese la medicina mas vendida?

    • Avatar

      Fernando 26 septiembre. 2017 - 16:49

      Hola Cielo,
      Los datos de los dos arrays no están relacionados. Si fuera un hash de este estilo:

      {amex: 1000, esitol: 1200, jaditor: 600, zainex: 400}

      Ahí podrías usar un método de Enumerable para encontrar el que tenga mayor valor.

Dejar un comentario

Toasty!