¿Por qué la ordenación "simple" en Kotlin rompe el código?
\n¡Hola, colega! Si alguna vez has escrito en Kotlin y has intentado ordenar una lista de tareas primero por fecha y luego por prioridad, seguramente te has encontrado con una situación en la que el resultado parece un conjunto aleatorio. No estás solo: la pregunta "Sort collection by multiple fields in Kotlin" ha acumulado miles de visitas en Stack Overflow. Parece sencillo, ¿verdad? Pero el diablo, como siempre, está en los detalles.
\nEn este artículo analizaremos los principales errores al ordenar por múltiples campos en Kotlin que incluso los desarrolladores experimentados cometen. Aprenderás cómo funciona sortedWith, por qué compareBy y thenBy son tus mejores amigos, y cómo no perder datos debido a null. ¡Allá vamos!
Error n.º 1: Ordenar como en Java — con Comparator manualmente
\nKotlin no es Java. Pero muchos, viniendo del mundo Java, escriben:
\nval list = listOf(\n User("Alice", 25),\n User("Bob", 20),\n User("Charlie", 25)\n)\n\nval sorted = list.sortedWith(Comparator { a, b ->\n val ageCompare = a.age.compareTo(b.age)\n if (ageCompare != 0) ageCompare else a.name.compareTo(b.name)\n})\n\nSí, funciona. Pero es terriblemente ilegible y es fácil perderse en cadenas de if-else. Kotlin ofrece una forma elegante: funciones comparadoras.
¿Cómo hacerlo correctamente?
\nval sorted = list.sortedWith(compareBy<User> { it.age }.thenBy { it.name })\n\nCorto, claro, sin lógica innecesaria. compareBy crea un comparador primario, y thenBy agrega campos secundarios. Así es la ordenación por múltiples campos en Kotlin en su forma ideal.
Error n.º 2: Ignorar null al ordenar
\nSupongamos que tenemos una lista de tareas donde deadline puede ser null. Si simplemente escribes:
tasks.sortedBy { it.deadline }\n\n…obtendrás un NullPointerException. Kotlin es estricto con null, y eso es genial. Pero, ¿qué hacer si null es algo normal?
\nSolución: nullsFirst / nullsLast
\nUsa envoltorios especiales:
\nval sorted = tasks.sortedWith(\n compareBy<Task>(nullsLast()) { it.deadline }\n .thenBy { it.priority }\n)\n\nAquí, nullsLast() coloca todas las fechas null al final de la lista. También existe nullsFirst() — para lo contrario. Esto resuelve el problema y tu ordenación de colecciones en Kotlin se vuelve segura.
Error n.º 3: Confundir sortedBy con sortedWith
\nMuchos principiantes escriben:
\nlist.sortedBy { it.age }.sortedBy { it.name }\n\nY se sorprenden de que la ordenación por nombre "anula" la ordenación por edad. sortedBy crea una nueva lista cada vez y ordena solo por un campo. La última llamada es la que manda.
¿Cómo corregirlo?
\nUsa sortedWith con la cadena de compareBy:
list.sortedWith(compareBy({ it.age }, { it.name }))\n\nO la versión multilínea para lógica compleja:
\nlist.sortedWith(compareBy<Person> { it.age }.thenByDescending { it.name })\n\nRecuerda: una llamada a sortedWith — una ordenación por múltiples campos.
\n\nError n.º 4: Ordenación en orden inverso — en el lugar equivocado
\nA veces es necesario ordenar por edad ascendente, pero por nombre descendente. El error típico:
\nlist.sortedByDescending { it.age }.sortedBy { it.name }\n\nDe nuevo el mismo problema: la segunda ordenación rompe la primera. El enfoque correcto:
\nval sorted = list.sortedWith(\n compareBy<Person> { it.age }\n .thenByDescending { it.name }\n)\n\nO para un control total:
\nval sorted = list.sortedWith(\n compareBy(Person::age).thenByDescending(Person::name)\n)\n<