diff --git a/1-js/05-data-types/07-map-set/01-array-unique-map/task.md b/1-js/05-data-types/07-map-set/01-array-unique-map/task.md index b11b8546d..b212e4a8a 100644 --- a/1-js/05-data-types/07-map-set/01-array-unique-map/task.md +++ b/1-js/05-data-types/07-map-set/01-array-unique-map/task.md @@ -19,9 +19,11 @@ let values = ["Hare", "Krishna", "Hare", "Krishna", "Krishna", "Krishna", "Hare", "Hare", ":-O" ]; -alert( unique(values) ); // Hare, Krishna, :-O +alert( unique(values) ); // Hare, Krishna, :-O ``` + P.D. Aquí se usan strings, pero pueden ser valores de cualquier tipo. -P.D.S. Use `Set` para almacenar valores únicos. + +P.D.S. Use `Set` para almacenar valores únicos. diff --git a/1-js/05-data-types/07-map-set/02-filter-anagrams/solution.md b/1-js/05-data-types/07-map-set/02-filter-anagrams/solution.md index 21fa385c1..e0c3b67de 100644 --- a/1-js/05-data-types/07-map-set/02-filter-anagrams/solution.md +++ b/1-js/05-data-types/07-map-set/02-filter-anagrams/solution.md @@ -1,4 +1,3 @@ - Para encontrar todos los anagramas, dividamos cada palabra en letras y las ordenamos. Cuando se clasifican las letras, todos los anagramas son iguales. Por ejemplo: @@ -10,7 +9,7 @@ cheaters, hectares, teachers -> aceehrst ... ``` -Utilizaremos las variantes ordenadas por letras como propiedades de Map para almacenar solo un valor por cada propiedad: +Utilizaremos las variantes ordenadas por letras como claves de Map para almacenar solo un valor por cada clave: ```js run function aclean(arr) { @@ -23,6 +22,7 @@ function aclean(arr) { */!* map.set(sorted, word); } + return Array.from(map.values()); } @@ -30,6 +30,7 @@ let arr = ["nap", "teachers", "cheaters", "PAN", "ear", "era", "hectares"]; alert( aclean(arr) ); ``` + La clasificación de letras se realiza mediante la cadena de llamadas en la línea `(*)`. Por conveniencia la dividimos en múltiples líneas: @@ -41,24 +42,23 @@ let sorted = arr[i] // PAN .sort() // ['a','n','p'] .join(''); // anp ``` + Dos palabras diferentes`'PAN'` y `'nap'` reciben la misma forma ordenada por letras `'anp'`. La siguiente línea pone la palabra en el Map: - ```js map.set(sorted, word); ``` -Si alguna vez volvemos a encontrar una palabra con la misma forma ordenada por letras, sobrescribiría el valor anterior con la misma propiedad en Map. Por lo tanto, siempre tendremos como máximo una palabra ordenada por letras. +Si alguna vez volvemos a encontrar una palabra con la misma forma ordenada por letras, sobrescribiría el valor anterior con la misma clave en Map. Por lo tanto, siempre tendremos como máximo una palabra ordenada por letras. -Al final, `Array.from (map.values())` toma un valor iterativo sobre los valores de Map (no necesitamos propiedades en el resultado) y devuelve un array de ellos. +Al final, `Array.from (map.values())` toma un valor iterativo sobre los valores de Map (no necesitamos claves en el resultado) y devuelve un array de ellos. -Aquí también podríamos usar un objeto plano en lugar del `Map`, porque las propiedades son strings. +Aquí también podríamos usar un objeto plano en lugar del `Map`, porque las claves son strings. Así es como puede verse la solución: - ```js run demo function aclean(arr) { let obj = {}; diff --git a/1-js/05-data-types/07-map-set/02-filter-anagrams/task.md b/1-js/05-data-types/07-map-set/02-filter-anagrams/task.md index 508c03203..5025b6c97 100644 --- a/1-js/05-data-types/07-map-set/02-filter-anagrams/task.md +++ b/1-js/05-data-types/07-map-set/02-filter-anagrams/task.md @@ -18,11 +18,11 @@ Escriba una función `aclean(arr)` que devuelva un array limpio de anagramas. Por ejemplo: - ```js let arr = ["nap", "teachers", "cheaters", "PAN", "ear", "era", "hectares"]; alert( aclean(arr) ); // "nap,teachers,ear" o "PAN,cheaters,era" ``` + Es decir, de cada grupo de anagramas debe quedar solo una palabra, sin importar cual. diff --git a/1-js/05-data-types/07-map-set/03-iterable-keys/solution.md b/1-js/05-data-types/07-map-set/03-iterable-keys/solution.md index 0d7fa149e..968c78dfd 100644 --- a/1-js/05-data-types/07-map-set/03-iterable-keys/solution.md +++ b/1-js/05-data-types/07-map-set/03-iterable-keys/solution.md @@ -3,6 +3,7 @@ Eso es porque `map.keys()` devuelve un iterable, pero no un array. Podemos convertirlo en un array usando `Array.from`: + ```js run let map = new Map(); diff --git a/1-js/05-data-types/07-map-set/03-iterable-keys/task.md b/1-js/05-data-types/07-map-set/03-iterable-keys/task.md index be3f1e034..c40a3f087 100644 --- a/1-js/05-data-types/07-map-set/03-iterable-keys/task.md +++ b/1-js/05-data-types/07-map-set/03-iterable-keys/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# Propiedades iterables +# Claves iterables -Nos gustaría obtener un array de `map.keys()` en una variable y luego aplicarle métodos específicos de array, ej. .push. +Nos gustaría obtener un array de `map.keys()` en una variable y luego aplicarle métodos específicos de array, ej. `.push`. Pero eso no funciona: @@ -22,4 +22,3 @@ keys.push("more"); ``` ¿Por qué? ¿Cómo podemos arreglar el código para que funcione `keys.push`? - diff --git a/1-js/05-data-types/07-map-set/article.md b/1-js/05-data-types/07-map-set/article.md index 0259217bd..6cc72b377 100644 --- a/1-js/05-data-types/07-map-set/article.md +++ b/1-js/05-data-types/07-map-set/article.md @@ -1,66 +1,69 @@ + # Map y Set Hasta este momento, hemos aprendido sobre las siguientes estructuras de datos: -- Objetos para almacenar colecciones de propiedades. -- Arrays para almacenar colecciones ordenadas. +- Objetos para almacenar colecciones de datos ordenadas mediante una clave. +- Arrays para almacenar colecciones ordenadas de datos. -Pero eso no es suficiente para la vida real. Por eso también existen Map y Set. +Pero eso no es suficiente para la vida real. Por eso también existen `Map` y `Set`. ## Map -[Map](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Map) es una colección de elementos de datos con propiedad, al igual que un objeto, y valor. Pero la principal diferencia es que Map permite propiedades de cualquier tipo. +[Map](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Map) es una colección de de datos relacionados, al igual que un `Objeto`. Pero la principal diferencia es que `Map` permite claves de cualquier tipo. Los métodos y propiedades son: - `new Map()` -- crea el mapa. -- `map.set(propiedad, valor)` -- almacena el valor para la propiedad. -- `map.get(propiedad)` -- devuelve el valor de la propiedad: será `undefined` si la `propiedad` no exite en Map. -- `map.has(propiedad)` -- devuelve`true` si la `propiedad` exite, y `false` si no existe. -- `map.delete(propiedad)` -- elimina los valores de la propiedad. +- `map.set(clave, valor)` -- almacena el valor para la clave. +- `map.get(clave)` -- devuelve el valor de la clave: será `undefined` si la `clave` no exite en Map. +- `map.has(clave)` -- devuelve `true` si la `clave` exite, y `false` si no existe. +- `map.delete(clave)` -- elimina los valores de la clave. - `map.clear()` -- limpia el Map. -- `map.size` -- retorna el número del elemento actual en el recuento de elementos en el Map. +- `map.size` -- retorna el número actual de elementos. Por ejemplo: ```js run let map = new Map(); -map.set('1', 'str1'); // un string como propiedad -map.set(1, 'num1'); // un número como propiedad -map.set(true, 'bool1'); // un booleano como propiedad -// recuerda el objeto regular? convertiría las propiedades en un string -// Map mantiene el tipo de dato en las propiedades, por lo que estas dos son diferentes: + +map.set('1', 'str1'); // un string como clave +map.set(1, 'num1'); // un número como clave +map.set(true, 'bool1'); // un booleano como clave + +// recuerda el objeto regular? convertiría las claves en un string +// Map mantiene el tipo de dato en las claves, por lo que estas dos son diferentes: alert( map.get(1) ); // 'num1' alert( map.get('1') ); // 'str1' alert( map.size ); // 3 ``` -Como podemos ver, a diferencia de los objetos, las propiedades no se convierten en strings. Cualquier tipo de propiedad es posible en un Map. +Como podemos ver, a diferencia de los objetos, las claves no se convierten en strings. Cualquier tipo de clave es posible en un Map. -```smart header="map[propiedad] no es la forma correcta para usar Map" -Aunque el map[propiedad] también funciona, por ejemplo, podemos establecer map[propiedad] = 2, esto es tratar a map como un objeto JavaScript simple, por lo que implica todas las limitaciones correspondientes (sin objetos como propiedad, etc.). +```smart header="map[clave] no es la forma correcta para usar Map" +Aunque el `map[clave]` también funciona, por ejemplo, podemos establecer `map[clave]` = 2, esto es tratar a `map` como un objeto JavaScript simple, por lo que implica todas las limitaciones correspondientes (sin objetos como clave, etc.). -Por lo tanto, deberíamos usar los métodos de `Map`: set, get, etc. +Por lo tanto, deberíamos usar los métodos de `Map`: `set`, `get`, etc. ``` -**El mapa también puede usar objetos como propiedades.** +**El mapa también puede usar objetos como claves.** Por ejemplo: ```js run -//John es un objeto let john = { name: "John" }; // para cada usuario, almacenemos el recuento de visitas let visitsCountMap = new Map(); -// John es la propiedad para el Map +// john es la clave para el Map visitsCountMap.set(john, 123); alert( visitsCountMap.get(john) ); // 123 ``` -El uso de objetos como propiedades es una de las características de `Map` más notables e importantes. Para las propiedades de tipo string, `Object` puede estar bien, pero no para las propiedades de tipo objeto. + +El uso de objetos como claves es una de las características de `Map` más notables e importantes. Para las claves de tipo string, `Object` puede estar bien, pero no para las claves de tipo objeto. Intentémoslo: @@ -69,16 +72,18 @@ let john = { name: "John" }; let visitsCountObj = {}; // intenta usar un objeto -visitsCountObj[john] = 123; // intenta usar el objeto john como propiedad +visitsCountObj[john] = 123; // intenta usar el objeto john como clave +*!* // Esto es lo que se escribió! alert( visitsCountObj["[object Object]"] ); // 123 +*/!* ``` -Como `visitsCountObj` es un objeto, convierte todas las propiedades, como John en string, por lo que tenemos la propiedad de tipo string `"[objeto Objeto]"`. Definitivamente no es lo que queremos. +Como `visitsCountObj` es un objeto, convierte todas las claves, como `john` en string, por lo que tenemos la clave de tipo string `"[objeto Objeto]"`. Definitivamente no es lo que queremos. -```smart header="Cómo `Map` compara las propiedades" -`Map` utiliza el algoritmo [SameValueZero](https://tc39.es/ecma262/#sec-samevaluezero). Es aproximadamente lo mismo que la igualdad estricta `===`, pero la diferencia es que `NaN` se considera igual a `NaN`. Por lo tanto, `NaN` también se puede usar como propiedad. +```smart header="Cómo `Map` compara las claves" +Para probar la equivalencia de claves, `Map` utiliza el algoritmo [SameValueZero](https://tc39.es/ecma262/#sec-samevaluezero). Es aproximadamente lo mismo que la igualdad estricta `===`, pero la diferencia es que `NaN` se considera igual a `NaN`. Por lo tanto, `NaN` también se puede usar como clave. Este algoritmo no se puede cambiar ni personalizar. ``` @@ -93,12 +98,14 @@ map.set('1', 'str1') ``` ```` + ## Iteración sobre Map -Para recorrer un `Map`, hay 3 métodos: -- `map.keys()` – devuelve un iterable para las propiedades. +Para recorrer un `map`, hay 3 métodos: + +- `map.keys()` – devuelve un iterable para las claves. - `map.values()` – devuelve un iterable para los valores. -- `map.entries()` – devuelve un iterable para las entradas `[propiedad, valor]`, se usa por defecto en `for..of`. +- `map.entries()` – devuelve un iterable para las entradas `[clave, valor]`, se usa por defecto en `for..of`. Por ejemplo: @@ -109,22 +116,22 @@ let recipeMap = new Map([ ['cebollas', 50] ]); -// iterando sobre las propiedades (verduras) +// iterando sobre las claves (verduras) for (let vegetable of recipeMap.keys()) { alert(vegetable); // pepino, tomates, cebollas } // iterando sobre los valores (precios) - for (let amount of recipeMap.values()) { alert(amount); // 500, 350, 50 } -// iterando sobre las entradas [propiedad, valor] +// iterando sobre las entradas [clave, valor] for (let entry of recipeMap) { // lo mismo que recipeMap.entries() alert(entry); // pepino,500 (etc) } ``` + ```smart header="Se utiliza el orden de inserción." La iteración va en el mismo orden en que se insertaron los valores. `Map` conserva este orden, a diferencia de un `Objeto` normal. ``` @@ -132,7 +139,7 @@ La iteración va en el mismo orden en que se insertaron los valores. `Map` conse Además de eso, `Map` tiene un método `forEach` incorporado, similar a `Array`: ```js -// recorre la función para cada par (propiedad, valor) +// recorre la función para cada par (clave, valor) recipeMap.forEach( (value, key, map) => { alert(`${key}: ${value}`); // pepino: 500 etc }); @@ -140,10 +147,10 @@ recipeMap.forEach( (value, key, map) => { ## Object.entries: Map desde Objeto -Cuando se crea un `Map`, podemos pasar un array (u otro iterable) con pares propiedad / valor para la inicialización, de esta manera: +Cuando se crea un `Map`, podemos pasar un array (u otro iterable) con pares clave / valor para la inicialización, de esta manera: ```js run -// array de [propiedad, valor] +// array de [clave, valor] let map = new Map([ ['1', 'str1'], [1, 'num1'], @@ -152,7 +159,8 @@ let map = new Map([ alert( map.get('1') ); // str1 ``` -Aquí hay un método incorporado [Object.entries(obj)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/entries) que devuelve un array de pares propiedad / valor para un objeto exactamente en ese formato. + +Aquí hay un método incorporado [Object.entries(obj)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/entries) que devuelve un array de pares clave / valor para un objeto exactamente en ese formato. Entonces podemos inicializar un mapa desde un objeto como este: @@ -162,17 +170,21 @@ let obj = { age: 30 }; +*!* let map = new Map(Object.entries(obj)); +*/!* alert( map.get('name') ); // John ``` -Aquí, `Object.entries` devuelve el array de pares propiedad / valor: [["" name "," John "], [" age ", 30]]. Eso es lo que necesita `Map`. +Aquí, `Object.entries` devuelve el array de pares clave / valor: [["" name "," John "], [" age ", 30]]. Eso es lo que necesita `Map`. + ## Object.fromEntries: Objeto desde Map + Acabamos de ver cómo crear un `Map` a partir de un objeto simple con `Object.entries (obj).` -Existe el método `Object.fromEntries` que hace lo contrario: dado un array de pares [propiedad, valor], crea un objeto a partir de ellos: +Existe el método `Object.fromEntries` que hace lo contrario: dado un array de pares [clave, valor], crea un objeto a partir de ellos: ```js run let prices = Object.fromEntries([ @@ -181,10 +193,11 @@ let prices = Object.fromEntries([ ['meat', 4] ]); -// ahora prices es un objeto = { banana: 1, orange: 2, meat: 4 } +// ahora prices = { banana: 1, orange: 2, meat: 4 } alert(prices.orange); // 2 ``` + Podemos usar `Object.fromEntries` para obtener un objeto plano de `Map`. Ej. almacenamos los datos en un `Map`, pero necesitamos pasarlos a un código de terceros que espera un objeto simple. @@ -197,7 +210,9 @@ map.set('banana', 1); map.set('orange', 2); map.set('meat', 4); +*!* let obj = Object.fromEntries(map.entries()); // hace un objeto simple +*/!* // Hecho! // obj = { banana: 1, orange: 2, meat: 4 } @@ -205,17 +220,18 @@ let obj = Object.fromEntries(map.entries()); // hace un objeto simple alert(obj.orange); // 2 ``` -Una llamada a `map.entries()` devuelve un array de pares propiedad / valor, exactamente en el formato correcto para `Object.fromEntries.` - -También podríamos acortar la línea 6 del ejemplo anterior: +Una llamada a `map.entries()` devuelve un array de pares clave / valor, exactamente en el formato correcto para `Object.fromEntries.` +También podríamos acortar la línea `(*)`: ```js let obj = Object.fromEntries(map); // omitimos .entries() ``` -Es lo mismo, porque `Object.fromEntries` espera un objeto iterable como argumento. No necesariamente un array. Y la iteración estándar para el `Map` devuelve los mismos pares propiedad / valor que `map.entries()`. Entonces obtenemos un objeto simple con las mismas propiedades / valores que `Map`. + +Es lo mismo, porque `Object.fromEntries` espera un objeto iterable como argumento. No necesariamente un array. Y la iteración estándar para el `Map` devuelve los mismos pares clave / valor que `map.entries()`. Entonces obtenemos un objeto simple con las mismas claves / valores que `Map`. ## Set -`Set` es una colección de tipo especial: "conjunto de valores" (sin propiedades), donde cada valor puede aparecer solo una vez. + +`Set` es una colección de tipo especial: "conjunto de valores" (sin claves), donde cada valor puede aparecer solo una vez. Sus principales métodos son: @@ -240,7 +256,6 @@ let pete = { name: "Pete" }; let mary = { name: "Mary" }; // visitas, algunos usuarios lo hacen varias veces - set.add(john); set.add(pete); set.add(mary); @@ -254,9 +269,11 @@ for (let user of set) { alert(user.name); // John (luego Pete y Mary) } ``` + La alternativa a `Set` podría ser un array de usuarios y el código para verificar si hay duplicados en cada inserción usando [arr.find](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/find). Pero el rendimiento sería mucho peor, porque este método recorre el array completo comprobando cada elemento. `Set` está mucho mejor optimizado internamente para verificaciones de unicidad. ## Iteración sobre Set + Podemos recorrer `Set` con `for..of` o usando `forEach`: ```js run @@ -265,7 +282,6 @@ let set = new Set(["oranges", "apples", "bananas"]); for (let value of set) alert(value); // lo mismo que forEach: - set.forEach((value, valueAgain, set) => { alert(value); }); @@ -277,29 +293,33 @@ Eso es por compatibilidad con `Map` donde la función callback tiene tres argume También soporta los mismos métodos que `Map` tiene para los iteradores: -- `set.keys()` – devuelve un iterable para las propiedades. +- `set.keys()` – devuelve un iterable para las claves. - `set.values()` – lo mismo que `set.keys()`, por su compatibilidad con `Map`. -- `set.entries()` – devuelve un iterable para las entradas `[propiedad, valor]`, por su compatibilidad con `Map`. +- `set.entries()` – devuelve un iterable para las entradas `[clave, valor]`, por su compatibilidad con `Map`. ## Resumen -`Map`: es una colección de valores con propiedad. + +`Map`: es una colección de valores con clave. Métodos y propiedades: + - `new Map()` -- crea el mapa. -- `map.set(propiedad, valor)` -- almacena el valor para la propiedad. -- `map.get(propiedad)` -- devuelve el valor de la propiedad: será `undefined` si la `propiedad` no exite en Map. -- `map.has(propiedad)` -- devuelve`true` si la `propiedad` exite, y `false` si no existe. -- `map.delete(propiedad)` -- elimina los valores de la propiedad. +- `map.set(clave, valor)` -- almacena el valor para la clave. +- `map.get(clave)` -- devuelve el valor de la clave: será `undefined` si la `clave` no exite en Map. +- `map.has(clave)` -- devuelve`true` si la `clave` exite, y `false` si no existe. +- `map.delete(clave)` -- elimina los valores de la clave. - `map.clear()` -- limpia el Map. - `map.size` -- retorna el número del elemento actual en el recuento de elementos en el Map. La diferencia con `Objeto` regular: -- Cualquier propiedad, los objetos tambien pueden ser propiedads. -- Adicionalmente tiene métodos que nos convienen, como la propiedad `size`. + +- Cualquier clave, los objetos tambien pueden ser claves. +- Adicionalmente tiene métodos que nos convienen, como la clave `size`. `Set`: es una colección de valores únicos. Métodos y propiedades: + - `new Set(iterable)` -- crea el set y, si se proporciona un objeto iterable (generalmente un array), copia los valores del mismo en el set. - `set.add(valor)` -- agrega un valor, devuelve el set en sí. - `set.delete(valor)` -- elimina el valor, devuelve `true` si `valor` existe al momento de la llamada, si no, devuelve `false`. @@ -308,4 +328,3 @@ Métodos y propiedades: - `set.size` -- es el contador de los elementos. La iteración sobre `Map` y `Set` siempre está en el orden de inserción, por lo que no podemos decir que estas colecciones están desordenadas, pero no podemos reordenar elementos u obtener un elemento directamente por su número. -