Antecedentes: Recientemente trabajé en un proyecto Vue.js que permite a un usuario buscar una lista de servicios municipales ubicados cerca. Cuando el cliente me entregó el proyecto, realicé algunas búsquedas y noté entradas duplicadas en la lista de resultados. Efectivamente, cuando abrí la consola del navegador, vi un mensaje de error quejándose de claves duplicadas en un bucle v-for. Dado que la lista proviene directamente de una solicitud GET al backend del sistema, creo que un error en el servicio backend es responsable de la introducción de estos elementos duplicados. Otra agencia escribió el código de back-end, y no estamos seguros de cuándo podremos tocar la base, así que decidí filtrar los duplicados en el front-end por el momento. Se me ocurrieron dos enfoques. Mi primer enfoque: el código itera a través de la lista original de elementos y agrega a una nueva matriz cualquier elemento que aún no exista en la nueva matriz.
<template> <div> <div v-for="result in uniqueResults"> {{ result }} </div> ... </div> </template> <script> ... computed: { uniqueResults() { let uniqueItems = []; for(var i = 0; i < this.items.length; i++) { if (uniqueItems.findIndex((uniqueItem) => uniqueItem.id === this.items[i].id) === -1) { uniqueItems.push(this.items[i]) } } return uniqueItems; } ... </script> Este enfoque inicial funcionó correctamente, pero ralentiza el tiempo de carga de la página. Esto tiene sentido, ya que para cada elemento de la lista, estamos comprobando si el elemento anterior es un duplicado. Esto se traduce en una complejidad de tiempo asintótica de O(n²). Me preguntaba si esto podría lograrse en tiempo lineal. 2. En mi segundo enfoque, utilicé el método Array.prototype.filter() de Javascript y aproveché el segundo parámetro de devolución de llamada, position, para realizar la deduplicación. Este código funciona devolviendo solo la primera aparición de cada elemento y filtrando el resto. El secreto es que el método indexOf(foo) devuelve el índice de solo la primera instancia de foo en la matriz. Aunque este enfoque también se ejecuta en tiempo O(n²) (dado que iteramos a través de cada elemento de la lista e indexOf() tiene una complejidad de tiempo lineal), en realidad parece funcionar más rápido en la práctica. ¿Por qué este enfoque funciona más rápido? ¿Y hay un enfoque con una mejor complejidad de tiempo que O (n ^ 2)?
uniqueResults() { return this.items.filter((item, position) => (this.items.map(i => i.id).indexOf(item.id) === position)); }Entonces, si por ejemplo, una matriz contiene los siguientes valores... [{nombre: Bob, id: 8},{nombre: Alice, id: 4}, {nombre: Alice, id: 4}], esto mapea [8,4 ,4]. Al iterar a través de la matriz, la función de filtro comienza en el elemento 0. indexOf(8) = 0 por lo que {name: Bob, id: 8} se incluirá en el resultado. Luego, la función de filtro examina el elemento en el índice 1. indexOf(4) = 1, por lo que {name: Alice, id: 4} también se incluirá en el resultado. Finalmente, la función de filtro examina el elemento en el índice 2. indexOf(4) = 1 != 2, por lo que la segunda instancia del objeto {name: Alice, id: 4} se filtrará del resultado.