fixes
This commit is contained in:
parent
6782931a9e
commit
85e67ebb5b
4 changed files with 133 additions and 122 deletions
|
@ -92,35 +92,6 @@ map.set('1', 'str1')
|
|||
```
|
||||
````
|
||||
|
||||
## Map from Object
|
||||
|
||||
When a `Map` is created, we can pass an array (or another iterable) with key-value pairs for initialization, like this:
|
||||
|
||||
```js
|
||||
// array of [key, value] pairs
|
||||
let map = new Map([
|
||||
['1', 'str1'],
|
||||
[1, 'num1'],
|
||||
[true, 'bool1']
|
||||
]);
|
||||
```
|
||||
|
||||
If we have a plain object, and we'd like to create a `Map` from it, then we can use built-in method [Object.entries(obj)](mdn:js/Object/entries) that returns an array of key/value pairs for an object exactly in that format.
|
||||
|
||||
So we can initialize a map from an object like this:
|
||||
|
||||
```js
|
||||
let obj = {
|
||||
name: "John",
|
||||
age: 30
|
||||
};
|
||||
|
||||
*!*
|
||||
let map = new Map(Object.entries(obj));
|
||||
*/!*
|
||||
```
|
||||
|
||||
Here, `Object.entries` returns the array of key/value pairs: `[ ["name","John"], ["age", 30] ]`. That's what `Map` needs.
|
||||
|
||||
## Iteration over Map
|
||||
|
||||
|
@ -168,6 +139,90 @@ recipeMap.forEach( (value, key, map) => {
|
|||
});
|
||||
```
|
||||
|
||||
## Object.entries: Map from Object
|
||||
|
||||
When a `Map` is created, we can pass an array (or another iterable) with key/value pairs for initialization, like this:
|
||||
|
||||
```js run
|
||||
// array of [key, value] pairs
|
||||
let map = new Map([
|
||||
['1', 'str1'],
|
||||
[1, 'num1'],
|
||||
[true, 'bool1']
|
||||
]);
|
||||
|
||||
alert( map.get('1') ); // str1
|
||||
```
|
||||
|
||||
If we have a plain object, and we'd like to create a `Map` from it, then we can use built-in method [Object.entries(obj)](mdn:js/Object/entries) that returns an array of key/value pairs for an object exactly in that format.
|
||||
|
||||
So we can create a map from an object like this:
|
||||
|
||||
```js run
|
||||
let obj = {
|
||||
name: "John",
|
||||
age: 30
|
||||
};
|
||||
|
||||
*!*
|
||||
let map = new Map(Object.entries(obj));
|
||||
*/!*
|
||||
|
||||
alert( map.get('name') ); // John
|
||||
```
|
||||
|
||||
Here, `Object.entries` returns the array of key/value pairs: `[ ["name","John"], ["age", 30] ]`. That's what `Map` needs.
|
||||
|
||||
|
||||
## Object.fromEntries: Object from Map
|
||||
|
||||
We've just seen how to create `Map` from a plain object with `Object.entries(obj)`.
|
||||
|
||||
There's `Object.fromEntries` method that does the reverse: given an array of `[key, value]` pairs, it creates an object from them:
|
||||
|
||||
```js run
|
||||
let prices = Object.fromEntries([
|
||||
['banana', 1],
|
||||
['orange', 2],
|
||||
['meat', 4]
|
||||
]);
|
||||
|
||||
// now prices = { banana: 1, orange: 2, meat: 4 }
|
||||
|
||||
alert(prices.orange); // 2
|
||||
```
|
||||
|
||||
We can use `Object.fromEntries` to get an plain object from `Map`.
|
||||
|
||||
E.g. we store the data in a `Map`, but we need to pass it to a 3rd-party code that expects a plain object.
|
||||
|
||||
Here we go:
|
||||
|
||||
```js run
|
||||
let map = new Map();
|
||||
map.set('banana', 1);
|
||||
map.set('orange', 2);
|
||||
map.set('meat', 4);
|
||||
|
||||
*!*
|
||||
let obj = Object.fromEntries(map.entries()); // make a plain object (*)
|
||||
*/!*
|
||||
|
||||
// done!
|
||||
// obj = { banana: 1, orange: 2, meat: 4 }
|
||||
|
||||
alert(obj.orange); // 2
|
||||
```
|
||||
|
||||
A call to `map.entries()` returns an array of key/value pairs, exactly in the right format for `Object.fromEntries`.
|
||||
|
||||
We could also make line `(*)` shorter:
|
||||
```js
|
||||
let obj = Object.fromEntries(map); // omit .entries()
|
||||
```
|
||||
|
||||
That's the same, because `Object.fromEntries` expects an iterable object as the argument. Not necessarily an array. And the standard iteration for `map` returns same key/value pairs as `map.entries()`. So we get a plain object with same key/values as the `map`.
|
||||
|
||||
## Set
|
||||
|
||||
A `Set` is a special type collection - "set of values" (without keys), where each value may occur only once.
|
||||
|
|
|
@ -282,7 +282,7 @@ The most notable limitation of `WeakMap` and `WeakSet` is the absence of iterati
|
|||
|
||||
`WeakMap` is `Map`-like collection that allows only objects as keys and removes them together with associated value once they become inaccessible by other means.
|
||||
|
||||
`WeakSet` is `Set`-like collection that only stores objects and removes them once they become inaccessible by other means.
|
||||
`WeakSet` is `Set`-like collection that stores only objects and removes them once they become inaccessible by other means.
|
||||
|
||||
Both of them do not support methods and properties that refer to all keys or their count. Only individial operations are allowed.
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ For plain objects, the following methods are available:
|
|||
- [Object.values(obj)](mdn:js/Object/values) -- returns an array of values.
|
||||
- [Object.entries(obj)](mdn:js/Object/entries) -- returns an array of `[key, value]` pairs.
|
||||
|
||||
...But please note the distinctions (compared to map for example):
|
||||
Please note the distinctions (compared to map for example):
|
||||
|
||||
| | Map | Object |
|
||||
|-------------|------------------|--------------|
|
||||
|
@ -32,7 +32,7 @@ For plain objects, the following methods are available:
|
|||
|
||||
The first difference is that we have to call `Object.keys(obj)`, and not `obj.keys()`.
|
||||
|
||||
Why so? The main reason is flexibility. Remember, objects are a base of all complex structures in JavaScript. So we may have an object of our own like `order` that implements its own `order.values()` method. And we still can call `Object.values(order)` on it.
|
||||
Why so? The main reason is flexibility. Remember, objects are a base of all complex structures in JavaScript. So we may have an object of our own like `data` that implements its own `data.values()` method. And we still can call `Object.values(data)` on it.
|
||||
|
||||
The second difference is that `Object.*` methods return "real" array objects, not just an iterable. That's mainly for historical reasons.
|
||||
|
||||
|
@ -69,55 +69,21 @@ Just like a `for..in` loop, these methods ignore properties that use `Symbol(...
|
|||
Usually that's convenient. But if we want symbolic keys too, then there's a separate method [Object.getOwnPropertySymbols](mdn:js/Object/getOwnPropertySymbols) that returns an array of only symbolic keys. Also, there exist a method [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) that returns *all* keys.
|
||||
```
|
||||
|
||||
## Object.fromEntries to transform objects
|
||||
|
||||
Sometimes we need to perform a transformation of an object to `Map` and back.
|
||||
## Transforming objects
|
||||
|
||||
We already have `new Map(Object.entries(obj))` to make a `Map` from `obj`.
|
||||
Objects lack many methods that exist for arrays, e.g. `map`, `filter` and others.
|
||||
|
||||
The syntax of `Object.fromEntries` does the reverse. Given an array of `[key, value]` pairs, it creates an object:
|
||||
If we'd like to apply them, then we can use `Object.entries` followed `Object.fromEntries`:
|
||||
|
||||
1. Use `Object.entries(obj)` to get an array of key/value pairs from `obj`.
|
||||
2. Use array methods on that array, e.g. `map`.
|
||||
3. Use `Object.fromEntries(array)` on the resulting array to turn it back into an object.
|
||||
|
||||
For example, we have an object with prices, and would like to double them:
|
||||
|
||||
```js run
|
||||
let prices = Object.fromEntries([
|
||||
['banana', 1],
|
||||
['orange', 2],
|
||||
['meat', 4]
|
||||
]);
|
||||
|
||||
// now prices = { banana: 1, orange: 2, meat: 4 }
|
||||
|
||||
alert(prices.orange); // 2
|
||||
```
|
||||
|
||||
Let's see practical applications.
|
||||
|
||||
For example, we'd like to create a new object with double prices from the existing one.
|
||||
|
||||
For arrays, we have `.map` method that allows to transform an array, but nothing like that for objects.
|
||||
|
||||
So we can use a loop:
|
||||
|
||||
```js run
|
||||
let prices = {
|
||||
banana: 1,
|
||||
orange: 2,
|
||||
meat: 4,
|
||||
};
|
||||
|
||||
let doublePrices = {};
|
||||
for(let [product, price] of Object.entries(prices)) {
|
||||
doublePrices[product] = price * 2;
|
||||
}
|
||||
|
||||
alert(doublePrices.meat); // 8
|
||||
```
|
||||
|
||||
...Or we can represent the object as an `Array` using `Object.entries`, then perform the operations with `map` (and potentially other array methods), and then go back using `Object.fromEntries`.
|
||||
|
||||
Let's do it for our object:
|
||||
|
||||
```js run
|
||||
let prices = {
|
||||
let users = {
|
||||
banana: 1,
|
||||
orange: 2,
|
||||
meat: 4,
|
||||
|
@ -135,21 +101,4 @@ alert(doublePrices.meat); // 8
|
|||
|
||||
It may look difficult from the first sight, but becomes easy to understand after you use it once or twice.
|
||||
|
||||
We also can use `fromEntries` to get an object from `Map`.
|
||||
|
||||
E.g. we have a `Map` of prices, but we need to pass it to a 3rd-party code that expects an object.
|
||||
|
||||
Here we go:
|
||||
|
||||
```js run
|
||||
let map = new Map();
|
||||
map.set('banana', 1);
|
||||
map.set('orange', 2);
|
||||
map.set('meat', 4);
|
||||
|
||||
let obj = Object.fromEntries(map);
|
||||
|
||||
// now obj = { banana: 1, orange: 2, meat: 4 }
|
||||
|
||||
alert(obj.orange); // 2
|
||||
```
|
||||
We can make powerful one-liners for more complex transforms this way. It's only important to keep balance, so that the code is still simple enough to understand it.
|
||||
|
|
|
@ -262,7 +262,7 @@ alert(height); // 200
|
|||
|
||||
Just like with arrays or function parameters, default values can be any expressions or even function calls. They will be evaluated if the value is not provided.
|
||||
|
||||
The code below asks for width, but not the title.
|
||||
In the code below `prompt` asks for `width`, but not for `title`:
|
||||
|
||||
```js run
|
||||
let options = {
|
||||
|
@ -293,6 +293,21 @@ alert(w); // 100
|
|||
alert(h); // 200
|
||||
```
|
||||
|
||||
If we have a complex object with many properties, we can extract only what we need:
|
||||
|
||||
```js run
|
||||
let options = {
|
||||
title: "Menu",
|
||||
width: 100,
|
||||
height: 200
|
||||
};
|
||||
|
||||
// only extract title as a variable
|
||||
let { title } = options;
|
||||
|
||||
alert(title); // Menu
|
||||
```
|
||||
|
||||
### The rest pattern "..."
|
||||
|
||||
What if the object has more properties than we have variables? Can we take some and then assign the "rest" somewhere?
|
||||
|
@ -319,8 +334,6 @@ alert(rest.height); // 200
|
|||
alert(rest.width); // 100
|
||||
```
|
||||
|
||||
|
||||
|
||||
````smart header="Gotcha if there's no `let`"
|
||||
In the examples above variables were declared right in the assignment: `let {…} = {…}`. Of course, we could use existing variables too, without `let`. But there's a catch.
|
||||
|
||||
|
@ -343,7 +356,9 @@ The problem is that JavaScript treats `{...}` in the main code flow (not inside
|
|||
}
|
||||
```
|
||||
|
||||
To show JavaScript that it's not a code block, we can make it a part of an expression by wrapping in parentheses `(...)`:
|
||||
So here JavaScript assumes that we have a code block, but why there's an error. We have destructuring instead.
|
||||
|
||||
To show JavaScript that it's not a code block, we can wrap the expression in parentheses `(...)`:
|
||||
|
||||
```js run
|
||||
let title, width, height;
|
||||
|
@ -353,14 +368,13 @@ let title, width, height;
|
|||
|
||||
alert( title ); // Menu
|
||||
```
|
||||
|
||||
````
|
||||
|
||||
## Nested destructuring
|
||||
|
||||
If an object or an array contain other objects and arrays, we can use more complex left-side patterns to extract deeper portions.
|
||||
If an object or an array contain other nested objects and arrays, we can use more complex left-side patterns to extract deeper portions.
|
||||
|
||||
In the code below `options` has another object in the property `size` and an array in the property `items`. The pattern at the left side of the assignment has the same structure:
|
||||
In the code below `options` has another object in the property `size` and an array in the property `items`. The pattern at the left side of the assignment has the same structure to extract values from them:
|
||||
|
||||
```js run
|
||||
let options = {
|
||||
|
@ -369,7 +383,7 @@ let options = {
|
|||
height: 200
|
||||
},
|
||||
items: ["Cake", "Donut"],
|
||||
extra: true // something extra that we will not destruct
|
||||
extra: true
|
||||
};
|
||||
|
||||
// destructuring assignment split in multiple lines for clarity
|
||||
|
@ -389,20 +403,13 @@ alert(item1); // Cake
|
|||
alert(item2); // Donut
|
||||
```
|
||||
|
||||
The whole `options` object except `extra` that was not mentioned, is assigned to corresponding variables.
|
||||
|
||||
Note that `size` and `items` itself is not destructured.
|
||||
The whole `options` object except `extra` that was not mentioned, is assigned to corresponding variables:
|
||||
|
||||

|
||||
|
||||
Finally, we have `width`, `height`, `item1`, `item2` and `title` from the default value.
|
||||
|
||||
If we have a complex object with many properties, we can extract only what we need:
|
||||
|
||||
```js
|
||||
// take size as a whole into a variable, ignore the rest
|
||||
let { size } = options;
|
||||
```
|
||||
Note that there are no variables for `size` and `items`, as we take their content instead.
|
||||
|
||||
## Smart function parameters
|
||||
|
||||
|
@ -421,6 +428,7 @@ In real-life, the problem is how to remember the order of arguments. Usually IDE
|
|||
Like this?
|
||||
|
||||
```js
|
||||
// undefined where detauls values are fine
|
||||
showMenu("My Menu", undefined, undefined, ["Item1", "Item2"])
|
||||
```
|
||||
|
||||
|
@ -472,29 +480,28 @@ function showMenu({
|
|||
showMenu(options);
|
||||
```
|
||||
|
||||
The syntax is the same as for a destructuring assignment:
|
||||
The full syntax is the same as for a destructuring assignment:
|
||||
```js
|
||||
function({
|
||||
incomingProperty: parameterName = defaultValue
|
||||
incomingProperty: varName = defaultValue
|
||||
...
|
||||
})
|
||||
```
|
||||
|
||||
Then, for an object of parameters, there will be a variable `varName` for property `incomingProperty`, with `defaultValue` by default.
|
||||
|
||||
Please note that such destructuring assumes that `showMenu()` does have an argument. If we want all values by default, then we should specify an empty object:
|
||||
|
||||
```js
|
||||
showMenu({});
|
||||
|
||||
showMenu({}); // ok, all values are default
|
||||
|
||||
showMenu(); // this would give an error
|
||||
```
|
||||
|
||||
We can fix this by making `{}` the default value for the whole destructuring thing:
|
||||
|
||||
We can fix this by making `{}` the default value for the whole object of parameters:
|
||||
|
||||
```js run
|
||||
// simplified parameters a bit for clarity
|
||||
function showMenu(*!*{ title = "Menu", width = 100, height = 200 } = {}*/!*) {
|
||||
function showMenu({ title = "Menu", width = 100, height = 200 }*!* = {}*/!*) {
|
||||
alert( `${title} ${width} ${height}` );
|
||||
}
|
||||
|
||||
|
@ -506,7 +513,7 @@ In the code above, the whole arguments object is `{}` by default, so there's alw
|
|||
## Summary
|
||||
|
||||
- Destructuring assignment allows for instantly mapping an object or array onto many variables.
|
||||
- The object syntax:
|
||||
- The full object syntax:
|
||||
```js
|
||||
let {prop : varName = default, ...rest} = object
|
||||
```
|
||||
|
@ -515,7 +522,7 @@ In the code above, the whole arguments object is `{}` by default, so there's alw
|
|||
|
||||
Object properties that have no mapping are copied to the `rest` object.
|
||||
|
||||
- The array syntax:
|
||||
- The full array syntax:
|
||||
|
||||
```js
|
||||
let [item1 = default, item2, ...rest] = array
|
||||
|
@ -523,4 +530,4 @@ In the code above, the whole arguments object is `{}` by default, so there's alw
|
|||
|
||||
The first item goes to `item1`; the second goes into `item2`, all the rest makes the array `rest`.
|
||||
|
||||
- For more complex cases, the left side must have the same structure as the right one.
|
||||
- It's possible to extract data from nested arrays/objects, for that the left side must have the same structure as the right one.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue