Aprende a implementar y depurar el método get en una hash table en JavaScript con seguridad y claridad. Verás cómo calcular la address a partir del key, recorrer el bucket correcto y devolver el value adecuado incluso cuando hay colisiones. Todo con ejemplos simples y código listo para usar.
¿Qué resuelve get en una hash table?
El método get permite recuperar el value asociado a un key. La idea central: el hash es determinista, por lo que un mismo key siempre genera la misma address. Con esa address accedemos al bucket (un array de arrays), donde cada elemento es una pareja [key, value].
Hash determinista: mismo key, misma address.
Buckets: listas internas donde pueden convivir varias parejas por colisión.
Búsqueda puntual: se recorre solo el bucket objetivo, no toda la estructura.
Así, get ubica el bucket correcto y hace una búsqueda lineal hasta encontrar el key y devolver su value.
¿Cómo implementar get con manejo de colisiones en JavaScript?
La implementación se basa en cinco pasos: calcular la address, obtener el bucket, validar su existencia, recorrer con for y comparar el key en cada sublista. Si no hay coincidencia, se retorna undefined.
classHashTable{constructor(size){this.data=newArray(size);}hash(key){// implementación existente del hash.}set(key, value){// implementación existente del set.}get(key){const address =this.hash(key);const currentBucket =this.data[address];if(currentBucket){for(let i =0; i < currentBucket.length; i++){if(currentBucket[i][0]=== key){return currentBucket[i][1];}}}returnundefined;}}
Calcula la address con this.hash(key).
Toma el bucket con this.data[address].
Si el bucket existe, recórrelo con un loopfor.
Compara currentBucket[i][0] === key en cada pareja [key, value].
Devuelve currentBucket[i][1] al encontrar coincidencia.
Si no hay coincidencia, retorna undefined.
¿Cómo recorre el loop for el bucket?
Cada bucket es una lista de listas. El índice i apunta a una sublista. En cada iteración se valida: si el elemento cero de esa sublista ([0]) es el key buscado, se regresa el elemento uno ([1]), que es el value. Si no, el loop continúa.
¿Qué retorna si el key no existe?
Si el bucket está vacío o no se encontró coincidencia durante el recorrido, la función regresa undefined. Esta señal es clara para indicar ausencia del elemento.
¿Cómo validar y probar con ejemplos usando set y get?
Primero, crea una instancia con un tamaño fijo para visualizar los buckets y posibles colisiones. Por ejemplo, 50 buckets para tener un espacio limitado y notar cuándo varios keys caen en la misma address.
const table =newHashTable(50);table.set('Diego',23);// ejemplo ilustrativo.table.set('Mariana',98);// puede colisionar con Diego.table.set('Adriana',2000);// otro par key-value.console.log(table.get('Diego'));// 23console.log(table.get('Mariana'));// 98console.log(table.get('Adriana'));// 2000console.log(table.get('X'));// undefined
Si hay colisión, el bucket tendrá varias parejas [key, value] y el for localizará la correcta.
La estructura interna luce así: [['Diego', 23], ['Mariana', 98]]. Cada sublista es una pareja.
La búsqueda es lineal dentro del bucket, lo que simplifica el manejo de colisiones.
¿Qué retos siguen: delete y keys?
Implementa delete(key): borra la pareja completa [key, value] al recibir un key.
Implementa keys(): devuelve todos los keys existentes en la hash table.
Comparte tu solución en comentarios: será útil ver distintos enfoques para estas operaciones y cómo optimizas el recorrido por buckets.
De forma grafica como se llega a cada elemento dentro de los buckets, y del currentBucket, a que arreglo de arreglos de clave, valor:
Saludos :)
Super
Gracias.
👩💻👨💻 Código de la clase y solución al reto de Remove y getAllKeys
classHashTable{constructor(size){this.data=newArray(size);}hashMethod(key){let hash =0;for(let i =0; i < key.length; i++){ hash =(hash + key.charCodeAt(i)* i)%this.data.length;}return hash;}set(key, value){const address =this.hashMethod(key);if(!this.data[address]){this.data[address]=[];}this.data[address].push([key, value]);returnthis.data;}get(key){const address =this.hashMethod(key);const currentBucket =this.data[address];if(currentBucket){for(let i =0; i < currentBucket.length; i++){if(currentBucket[i][0]=== key){return currentBucket[i][1];}}}returnundefined;}getAllKeys(){const keys =[];for(let i =0; i <this.data.length; i++){if(this.data[i]){for(let j =0; j <this.data[i].length; j++){ keys.push(this.data[i][j][0]);}}}return keys;}remove(key){const address =this.hashMethod(key);const currentBucket =this.data[address];if(currentBucket){for(let i =0; i < currentBucket.length; i++){if(currentBucket[i][0]=== key){const deletedValue =this.data[address][i];this.data[address].splice(i,1);return deletedValue;}}}returnundefined;}}
Me gusto tu opción del método remove(), las otras soluciones no están bien, porque no desplazan el índice cuando en un hash hay varios elementos.
¡Gracias por el comentario Jimmy! es un placer 😉
Creo que ha llegado el momento que rehagan TODOS los cursos de Javascript y que los haga Diego, este es su curso más complejo sobre Javascript que ha enseñado y sin embargo se entiende todo, hasta había entendido la lógica del matriz de arrays antes de que lo explique con el [ [], [], ...] y eso que yo no suelo entender a la primera...
Dejo el código del delete (Es prácticamente lo mismo que el get, pero en esta ocasión lo elimina con delete), le puse remove para que el linter de JS no me diera problemas ^^
remove(key){const address =this.hashMethod(key);const currentBucket =this.data[address];if(currentBucket){for(let i =0; i < currentBucket.length; i++){if(currentBucket[i][0]=== key){const element = currentBucket[i][1];delete currentBucket[i][1];return element;}}}returnundefined;}
Si haces delete solo del value, te van a quedar keys sin values que no hacen nada, deberías hacer el delete de currentBucket[i] completo, no solo del value [1]
Como dice David, solo estas borrando el key y dejando el value solo. De. igual forma, lo que dice David tampoco está correcto, ya que si haces el delete de currentBucket[i] estarías eliminando ambos valores pero dejando un espacio vacío, lo cual no queremos. Lo más optimo sería usar splice para cortar ese espacio de memoria y no agregar vacío.
mm no veo aun el potencial de este curso, veo muchas cosas repetidas de otros
En que otro u otros cursos de JS enseñan estas estructuras de datos?
¿Es en serio?
My solucion para el metodo getKeys y delete
/* eslint-disable no-restricted-syntax *//* eslint-disable no-plusplus */classHashTable{constructor(size){this.data=newArray(size);}hashMethod(key){let hash =0;for(let i =0; i < key.length; i++){ hash =(hash + key.charCodeAt(i)* i)%this.data.length;}return hash;}delete(key){const address =this.hashMethod(key);const currentBucket =this.data[address];if(currentBucket){for(let i =0; i < currentBucket.length; i++){if(currentBucket[i][0]=== key){const value = currentBucket[i]; currentBucket.splice(i,1);return value;}}}returnundefined;}keys(){const keys =[];this.data.forEach((bucket)=>{ bucket.forEach((keyPairValues)=>{ keys.push(keyPairValues[0]);});});return keys;}set(key, value){const address =this.hashMethod(key);if(!this.data[address]){this.data[address]=[];}this.data[address].push([key, value]);returnthis.data;}get(key){const address =this.hashMethod(key);const currentBucket =this.data[address];if(currentBucket){for(let i =0; i < currentBucket.length; i++){if(currentBucket[i][0]=== key){return currentBucket[i][1];}}}returnundefined;}}const myHashTable =newHashTable(50);myHashTable.set('franz',1997);myHashTable.set('laura',1998);myHashTable.set('mama',1966);myHashTable.set('elena',2000);myHashTable.get('franz');myHashTable.keys();myHashTable.delete('mama');myHashTable.keys();
¡Excelente! Se me había olvidado splice
Hay un detalle con la función de set, es que si haces dos veces un set de la misma llave entonces sólo lo vas a añadir al arreglo y cuando trates de hacer un get te devuelve el primer valor y nunca los que se añadieron de manera posterior, estuve viendo que en la implementación de Hash Table que tiene Java lo que hacen es sobreescribir el valor de la llave con el nuevo, más info al respecto acá). Para emular este comportamiento cambié un poco el método, y quedó así:
set(key, value){const address =this.hashMethod(key);if(!this.data[address]){this.data[address]=[];}if(this.data[address].find((element)=> element[0]=== key)){for(let i =0; i <this.data[address].length; i +=1){if(this.data[address][i][0]=== key){this.data[address][i][1]= value;}}}else{this.data[address].push([key, value]);}returnthis.data;}
También les comparto mis notas sobre éste módulo acá 😁.
// RECIBE LA LLAVE QUE BUSCAMOS CON SU VALORget(key){// LLAMAMOS LA FUNCION QUE NOS DEVUELVE UN HASHconst address =this.hashMethod(key)// EL ADDRESS EN DONDE SE ENCUENTRA LA INFORMACIÓNconst currentBucket =this.data[address]// SI ES TRUSTY if( currentBucket ){// DESDE 0 HASTA EL LARGO DE EL CURRENT BUCKETfor(let i =0; i < currentBucket.length; i++){// SI LA POSICION 0 DE EL ARREGLO ES IGUAL EL // KEY QUE NOS PASARONif( currentBucket[i][0]=== key){// DEVOLVEMOS LA POSICION 1 QUE APUNTA A SU VALORreturn currentBucket[i][1]}}}// DEVOLVEMOS UNDEFINEDreturnundefined}
me ayudaste a entender gracias :D
Es buena practica hacer un return en el for o se puede asignar ese valor a una variable y despues del for retornarlo, algo así
get(key){const address =this.hasMethod(key);const currentBucket =this.data[address];let value =undefined;if(currentBucket){for(let i =0; i < currentBucket.length; i++){if(currentBucket[i][0]=== key){ value = currentBucket[i][1];}}}return value;}
¡Hola!
El uso del return responde más al caso de uso o la necesidad que tengas, este ejemplo funciona muy bien cuando estás buscando un valor dado con el for y, al encontrarlo, evitas que el ciclo continúe con el return. Puedes regresar entonces el valor buscado.
¡Saludos!
¡Hola!
El uso del return responde más al caso de uso o la necesidad que tengas, este ejemplo funciona muy bien cuando estás buscando un valor dado con el for y, al encontrarlo, evitas que el ciclo continue con el return. Puedes regresar entonces el valor buscado.
¡Saludos!
Me ayudo mucho tu ejemplo, el unico error que vi es que sigue creado el bucket, con el hash ya creado, solo que vacio. Lo que yo hice es verificar si existen mas elementos en el bucket, en caso de que no, lo borro.
Resuelto el reto 😁
Añadí un método findPair a la clase y lo que hace es abstraer la lógica de búqueda del método get, para poder usarla a la vez en el método de delete, y no repetir código.
Y para aplicar la lógica de si buscar un value o eliminar, recibe un callback como segundo parámetro que inyecta el valor del currentBucket y del index actual
classHashTable{constructor(size){this.data=newArray(size);}hashMethod(key){let hash =0;for(let i =0; i < key.length; i++){ hash =(hash + key.charCodeAt(i)* i)%this.data.length;}return hash;}set(key, value){const address =this.hashMethod(key);if(!this.data[address]){this.data[address]=[];}this.data[address].push([key, value]);returnthis.data;}get(key){returnthis.findPair(key,(currentBucket, index)=> currentBucket[index][1]);}getAllKeys(){const allKeys =[];this.data.forEach((bucket)=>{for(let i =0; i < bucket.length; i++){ allKeys.push(bucket[i][0]);}})return allKeys;}delete(key){returnthis.findPair(key,(currentBucket, index)=>{const pair = currentBucket[index]; currentBucket.splice(index,1);return pair;})}findPair(key, callback){const address =this.hashMethod(key);const currentBucket =this.data[address];if(currentBucket){for(let i =0; i < currentBucket.length; i++){if(currentBucket[i][0]=== key){returncallback(currentBucket, i);}}}returnundefined;}}const myHashTable =newHashTable(50);myHashTable.set('Diego',1990);myHashTable.set("Mariana",1998);myHashTable.set("Alejandra",2000);
Esta es mi propuesta al método para obtener todas las keys de una hastable.
getKeys(){let keys =[];for(let i =0; i <this.data.length; i++){if(this.data[i]){for(let j =0; j <this.data[i].length; j++){ keys.push(this.data[i][j][0]);}}}return keys;}
Agradeceria su feedback
Yo lo hice exactamente igual hahah.
Aunque se entiende que en el metodo get, se hace una iteración por posibles colisiones, lo correcto es hacer la hash funcion lo más óptima posible, para evitar colisiones.
Hay que entender que lo que intenta solucionar la hash table, es que a la hora de tu buscar un elemento en la matriz, no tengas que integrar todos los elementos para buscar un solo valor, la idea es que gracias a la hash funcion ya tu recuperes ese elemento directamente, sin iterar.
Lo que se hace en el video es con fines didácticos y está bien, pero si hacen un for para buscar un valor en una hash table en un ámbito real, está mal.
Les comparto la forma en que lo hice jaja, y suerte :D
classHashTable{constructor(size){this.data=newArray(size);}hashMethod(key){let hash =0;for(let i =0; i < key; i++){ hash =(hash + key.chartCodeAt(i)* i )%this.data.length;}return hash;}set(key, value){const address =this.hashMethod(key);if(!this.data[address]){this.data[address]=[];}this.data[address].push([key, value]);returnthis.data;}get(key){const currentBucket =this.getCurrentBucket(key);if(currentBucket){for(let i =0; i < currentBucket.length; i++){if(currentBucket[i][0]=== key){return currentBucket[i][1];}}}returnundefined;}delete(key){const currentBucket =this.getCurrentBucket(key);let item;if(currentBucket){for(let i =0; i < currentBucket.length; i++){if(currentBucket[i][0]=== key){ item = currentBucket[i];this.data[address]= currentBucket.filter(j=> j[0]!== key);}}}return item ||undefined;}getKeys(){const address =this.hashMethod();const currentBucket =this.data[address];return currentBucket.map(i=> i[0]);}getValues(){const address =this.hashMethod();const currentBucket =this.data[address];return currentBucket.map(i=> i[1]);}getAll(){returnthis.getCurrentBucket().map(i=>({key:i[0],value: i[1]}));}find(value){const result =this.getCurrentBucket().filter(i=> i[1]=== value).map(i=>({key:i[0],value: i[1]}))return result.length>1? result : result[0]||undefined;}getCurrentBucket(key){let address = key ?this.hashMethod(key):this.hashMethod();returnthis.data[address];}}const myHashTable =newHashTable(50);
delete(key){const address =this.hashMethod(key)const currentBucket =this.data[address]console.log(`El elemento a borrar es: ${currentBucket}`)if(currentBucket){for(let i =0; i < currentBucket.length; i++){if(currentBucket[i][0]=== key){ currentBucket.splice(i,1)returntrue}}}returnfalse}getAllElements(){const elements =[]for(const bucket ofthis.data){if(bucket){for(const pair of bucket){ elements.push(pair)}}}return elements
}
Esas coalisiones son random?
He visto los cursos recomendados antes de tomar este, pero no entiendo como resolver los retos :/
¡Hola!
Puedes compartirnos tus avances así no lleges a la solución y acá la comunidad te ayudará, también puedes ver como lo han resuelto otros estudiantes en los aportes y tratar de hacerlo por tu cuenta.