Archive for 3 julio 2018

Ordenar un arreglo de objetos con base en un campo específico en Ruby

julio 3, 2018

Decidí hacer este post a raíz de algo que tuve que hacer cuando desarrollé el sistema Clepsidra. Lo que aquí muestro no es exactamente lo mismo, aquí está el concepto básico esperando que pueda servir para solucionar casos similares que se le puedan presentar a alguien más.

Supongamos que tenemos la siguiente información en formato json:

'[
  {"clave_asignatura":"C-101","nombre_asignatura":"Introducción al lenguaje de programción C","clave_profesor":"P210","nombre_profesor":"Dennis Ritchie"},
  {"clave_asignatura":"U-101","nombre_asignatura":"Unix avanzado","clave_profesor":"P210","nombre_profesor":"Dennis Ritchie"},
  {"clave_asignatura":"DB-101","nombre_asignatura":"Introducción a las bases de datos relacionales","clave_profesor":"P205","nombre_profesor":"Edgar Frank Codd"},
  {"clave_asignatura":"SO-101","nombre_asignatura":"Sistemas operativos","clave_profesor":"P225","nombre_profesor":"Linus Torvalds"},
  {"clave_asignatura":"C-101","nombre_asignatura":"Introducción al lenguaje de programción C","clave_profesor":"P235","nombre_profesor":"Brian Kernighan"},
  {"clave_asignatura":"SL-101","nombre_asignatura":"Introducción al software libre","clave_profesor":"P215","nombre_profesor":"Richard M. Stallman"},
  {"clave_asignatura":"C-101","nombre_asignatura":"Introducción al lenguaje de programción C","clave_profesor":"P230","nombre_profesor":"Ken Thompson"},
  {"clave_asignatura":"SO-101","nombre_asignatura":"Sistemas operativos","clave_profesor":"P220","nombre_profesor":"Andrew S. Tanenbaum"},
  {"clave_asignatura":"L-101","nombre_asignatura":"Linux básico","clave_profesor":"P225","nombre_profesor":"Linus Torvalds"},
  {"clave_asignatura":"U-101","nombre_asignatura":"Unix avanzado","clave_profesor":"P230","nombre_profesor":"Ken Thompson"}
 ]'

Son 10 registros, cada uno con 4 campos: clave_asignatura, nombre_asignatura, clave_profesor y nombre_profesor.

Esta información en formato json la pudimos haber obtenido desde un archivo o desde una base de datos, etc. eso es irrelevante. Como se puede observar, hay asignaturas que aparecen más de una vez, ya que una misma asignatura puede ser impartida por diferentes profesores, y debido a que un profesor puede impartir diferentes asignaturas, también hay profesores que aparecen más de una vez.

Supongamos que a partir de esta información tenemos que obtener una lista de las diferentes asignaturas ordenada por clave de asignatura. Es decir, en la lista ordenada, cada asignatura debe aparecer sólo una vez.

Lo primero que tenemos que hacer es convertir la información a un arreglo de hashes.

@json_data = '[
               {"clave_asignatura":"C-101","nombre_asignatura":"Introducción al lenguaje de programción C","clave_profesor":"P210","nombre_profesor":"Dennis Ritchie"},
               {"clave_asignatura":"U-101","nombre_asignatura":"Unix avanzado","clave_profesor":"P210","nombre_profesor":"Dennis Ritchie"},
               {"clave_asignatura":"DB-101","nombre_asignatura":"Introducción a las bases de datos relacionales","clave_profesor":"P205","nombre_profesor":"Edgar Frank Codd"},
               {"clave_asignatura":"SO-101","nombre_asignatura":"Sistemas operativos","clave_profesor":"P225","nombre_profesor":"Linus Torvalds"},
               {"clave_asignatura":"C-101","nombre_asignatura":"Introducción al lenguaje de programción C","clave_profesor":"P235","nombre_profesor":"Brian Kernighan"},
               {"clave_asignatura":"SL-101","nombre_asignatura":"Introducción al software libre","clave_profesor":"P215","nombre_profesor":"Richard M. Stallman"},
               {"clave_asignatura":"C-101","nombre_asignatura":"Introducción al lenguaje de programción C","clave_profesor":"P230","nombre_profesor":"Ken Thompson"},
               {"clave_asignatura":"SO-101","nombre_asignatura":"Sistemas operativos","clave_profesor":"P220","nombre_profesor":"Andrew S. Tanenbaum"},
               {"clave_asignatura":"L-101","nombre_asignatura":"Linux básico","clave_profesor":"P225","nombre_profesor":"Linus Torvalds"},
               {"clave_asignatura":"U-101","nombre_asignatura":"Unix avanzado","clave_profesor":"P230","nombre_profesor":"Ken Thompson"}
              ]'

@materias_profesores = JSON.parse(@json_data)

En el código anterior, la información en formato json la tenemos en la variable @json_data y ya como un arreglo de hashes la tenemos en la variable @materias_profesores

Si lo deseamos, podemos recorrer nuestro arreglo de hashes con el siguiente código para mostrar la información

@materias_profesores.each do |registro|
   puts registro["clave_asignatura"]+": "+registro["nombre_asignatura"]
   puts registro["clave_profesor"]+": "+registro["nombre_profesor"]
end

Para obtener la lista de asignaturas sin repetir lo que vamos a hacer es recorrer nuestro arreglo de hashes e ir agragando la clave de asignatura y su nombre a otro arreglo. Este nuevo arreglo será un arreglo de objetos en donde cada objeto sólo tendra dos campos, la clave de asignatura y el nombre de asignatura. Así que creamos la siguiente clase

class Materia
   attr_accessor :clave_asignatura, :nombre_asignatura
   
   def initialize(clave, nombre)
       @clave_asignatura = clave
       @nombre_asignatura = nombre
   end
end

Crearemos un arreglo llamado @asignaturas, en éste arreglo iremos agregando objetos de la clase Materia.

Como ya comenté antes, recorreremos nuestro arreglo @materias_profesores y antes de crear e insertar un objeto de la clase Materia a nuestro arreglo llamado @asignaturas, verificamos que no exista ya un objeto con esa información, así garantizamos que cada asignatura sólo aparezca una vez.

@asignaturas = []
@materias_profesores.each do |registro|
    # Sólo agrega la asignatura si no se encuentra ya en el arreglo @asignaturas
    if @asignaturas.all? {|asignatura| asignatura.clave_asignatura != registro["clave_asignatura"]}
       @asignaturas.push(Materia.new(registro["clave_asignatura"],registro["nombre_asignatura"]))
    end
end

Hasta ahora ya tenemos un arreglo de objetos de la clase Materia (nuestro arreglo llamado @asignaturas) en donde existe un objeto por cada una de las diferentes asignaturas.

Podemos usar el siguiente código si queremos mostrar el contenido de nuestro arreglo @asignaturas

puts "\n"
puts "="*20
puts "ASIGNATURAS"
puts "="*20
@asignaturas.each do |asignatura|
   puts asignatura.clave_asignatura+": "+asignatura.nombre_asignatura
end

Ahora sólo falta ordenar nuestro arreglo de objetos por el campo clave_asignatura

@asignaturas.sort! {|a, b| a.clave_asignatura <=> b.clave_asignatura}

Programa completo

#encoding: utf-8
require 'json'

class Materia
   attr_accessor :clave_asignatura, :nombre_asignatura
   
   def initialize(clave, nombre)
       @clave_asignatura = clave
       @nombre_asignatura = nombre
   end
end

@json_data = '[
               {"clave_asignatura":"C-101","nombre_asignatura":"Introducción al lenguaje de programción C","clave_profesor":"P210","nombre_profesor":"Dennis Ritchie"},
               {"clave_asignatura":"U-101","nombre_asignatura":"Unix avanzado","clave_profesor":"P210","nombre_profesor":"Dennis Ritchie"},
               {"clave_asignatura":"DB-101","nombre_asignatura":"Introducción a las bases de datos relacionales","clave_profesor":"P205","nombre_profesor":"Edgar Frank Codd"},
               {"clave_asignatura":"SO-101","nombre_asignatura":"Sistemas operativos","clave_profesor":"P225","nombre_profesor":"Linus Torvalds"},
               {"clave_asignatura":"C-101","nombre_asignatura":"Introducción al lenguaje de programción C","clave_profesor":"P235","nombre_profesor":"Brian Kernighan"},
               {"clave_asignatura":"SL-101","nombre_asignatura":"Introducción al software libre","clave_profesor":"P215","nombre_profesor":"Richard M. Stallman"},
               {"clave_asignatura":"C-101","nombre_asignatura":"Introducción al lenguaje de programción C","clave_profesor":"P230","nombre_profesor":"Ken Thompson"},
               {"clave_asignatura":"SO-101","nombre_asignatura":"Sistemas operativos","clave_profesor":"P220","nombre_profesor":"Andrew S. Tanenbaum"},
               {"clave_asignatura":"L-101","nombre_asignatura":"Linux básico","clave_profesor":"P225","nombre_profesor":"Linus Torvalds"},
               {"clave_asignatura":"U-101","nombre_asignatura":"Unix avanzado","clave_profesor":"P230","nombre_profesor":"Ken Thompson"}
              ]'

@materias_profesores = JSON.parse(@json_data)

@materias_profesores.each do |registro|
   puts registro["clave_asignatura"]+": "+registro["nombre_asignatura"]
   puts registro["clave_profesor"]+": "+registro["nombre_profesor"]
end

@asignaturas = []
@materias_profesores.each do |registro|
   # Sólo agrega la asignatura si no se encuentra ya en el arreglo @asignaturas
   if @asignaturas.all? {|asignatura| asignatura.clave_asignatura != registro["clave_asignatura"]}
      @asignaturas.push(Materia.new(registro["clave_asignatura"],registro["nombre_asignatura"]))
   end
end

puts "\n"
puts "="*20
puts "ASIGNATURAS"
puts "="*20
@asignaturas.each do |asignatura|
   puts asignatura.clave_asignatura+": "+asignatura.nombre_asignatura
end

# Ordena el arreglo @asignaturas por el campo clave_asignatura
@asignaturas.sort! {|a, b| a.clave_asignatura <=> b.clave_asignatura}

puts "\n"
puts "="*20
puts "ASIGNATURAS ORDENADAS POR CLAVE"
puts "="*20
@asignaturas.each do |asignatura|
   puts asignatura.clave_asignatura+": "+asignatura.nombre_asignatura
end

Quien quiera practicar, puede hacer una versión de este programa en donde utilizando la misma información en formato json, obtenga una lista de los diferentes profesores ordenada por el campo nombre_profesor

 

Anuncios