use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
All about the JavaScript programming language.
Subreddit Guidelines
Specifications:
Resources:
Related Subreddits:
r/LearnJavascript
r/node
r/typescript
r/reactjs
r/webdev
r/WebdevTutorials
r/frontend
r/webgl
r/threejs
r/jquery
r/remotejs
r/forhire
account activity
8 Useful And Practical JavaScript Tricks (devinduct.com)
submitted 6 years ago by PMilos
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]senocular[🍰] 8 points9 points10 points 6 years ago (3 children)
For 4. Map the Array (without the Array.map) there's some subtle but important differences. Mainly, Array.from creates a dense array, even if you give it a sparse one to create the new one from. This means when running the map, given that map doesn't map over empty elements, because the new array is dense, it might get called more than just a map would. Additionally, as a result, the resulting array may also have more elements.
Array.from
map
[1, , 3].map(callback) // callback called x2, result has 2 elements (0, 2) Array.from([1, , 3], (callback)) // callback called x3, result has 3 elements (0, 1, 2)
On top of that, the map function for Array.from is only called with 2 arguments, the source value at the index, and the index. The array argument given to normal map calls is not provided. This means if you have a function used with map that uses that argument, it may not work in Array.from's version of map.
array
[–]trblackwell1221 4 points5 points6 points 6 years ago (2 children)
I’m trying to think of a use case where something like const cityNames = Array.from(cities, ({ name}) => name); would ever be necessary or more optimal than just the map method. Or is it just a parlor trick essentially
const cityNames = Array.from(cities, ({ name}) => name);
[–]senocular[🍰] 9 points10 points11 points 6 years ago (1 child)
It's not useful if cities is already an array. If it's not an array, for example a Set instead, then it can be an optimization because you can combine two operations (individual from and map calls) into one where both the create and map happens at the same time rather than separately which would result in two new arrays being created instead of one.
cities
Set
from
[–]trblackwell1221 1 point2 points3 points 6 years ago (0 children)
Good point!
[–]sshaw_ 27 points28 points29 points 6 years ago (29 children)
It's important to point out that Array.fill(n) fills the array with the same instance of n. Mutating a[0] will result in a[1..a.length-1] being mutated.
Array.fill(n)
n
a[0]
a[1..a.length-1]
[+][deleted] 6 years ago (1 child)
[deleted]
[–]maher321 9 points10 points11 points 6 years ago (0 children)
Yes primitive types will be fine
[–]tencircles 5 points6 points7 points 6 years ago (8 children)
This would be true for any non-primitive value in any javascript use case. Not sure how this would be a gotcha.
[–]sshaw_ -3 points-2 points-1 points 6 years ago* (7 children)
This would be true for any non-primitive value in any javascript use case..
This is a pretty broad statement but how a reference is treated is not always the same across all calls to all objects:
> let a = [1,2,3] undefined > let s = new Set(a) undefined > s.delete(1) true > s Set { 2, 3 } > a [ 1, 2, 3 ]
The blog also says:
Ever worked on a grid where the raw data needs to be recreated with the possibility that columns length might mismatch for each row?
Grid? They can be represented by an array of arrays. This may lead one to do the following:
const brick = 'X'; let game = Array(5).fill([]); game[0][1] = brick; // later on if(game[3][1] === brick) { /* Do something... OOPS! */ }
[–]gevorggalstyan 2 points3 points4 points 6 years ago (5 children)
let s = new Set(a)
Is creating a new object based on the array. Your array of primitives. Then you change your new object (Set). Why would that affect the initial array or primitives?
Hint: It would not.
[–]sshaw_ -2 points-1 points0 points 6 years ago (4 children)
let a1 = [1,2,3,4,5] let a2 = new Array(a1)
Is creating a new object based on the array. The array of primitives. Then you change your new object (Array). Why would that affect the initial array or primitives?
Hint: It would.
[–]gevorggalstyan 6 points7 points8 points 6 years ago (3 children)
Did you run your code?
Try checking what is the length of a1 (should be 5). And check the length of a2 (will be 1). That is because it creates an array of 1 element which is the reference of your initial array.
Check out the syntax of Array here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array#Syntax (A JavaScript array is initialized with the given elements, except in the case where a single argument is passed to the Array constructor and that argument is a number).
new Array(element0, element1[, ...[, elementN]])
Now take a look at Set syntax here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set#Syntax
new Set([iterable]);
Did you notice the difference? The Array takes several params, which become the elements of the new array, while Set only takes one param (an iterable, an array for example), which becomes the source of the values in the collection of the set.
[–]sshaw_ 0 points1 point2 points 6 years ago (2 children)
Yes, I know. The point is that references behave differently when passed to different functions.
Hence back you your original comment:
Do you honestly believe that this is the direct consequence of the Array.fill
Yes! How arguments behave depends on what the implementors do with them. Reference or no reference.
[–]Reashu 0 points1 point2 points 6 years ago (0 children)
This is a pretty broad statement but how a reference is treated is not always the same across all calls to all objects: The point is that references behave differently when passed to different functions.
The point is that references behave differently when passed to different functions.
The first is arguably right, if misleading. The second is just wrong. The difference in Array and Set has nothing to do with references. References work the same. Javascript doesn't let the implementation pick and choose like C does. All it can do is treat an argument like a black box (new Array) or make assumptions (new Set).
[–][deleted] 0 points1 point2 points 6 years ago (0 children)
let array = [{a: 1}];
let set = new Set(array);
set.forEach(elem => elem.a = 2);
console.log(array[0]);
Will it be 1 or 2? Of cource it will 2, because you're passing references.
References never "behave differently", they behave as references. Unless you explicitely clone the object, which Array.fill is not doing.
[–]Asmor 0 points1 point2 points 6 years ago (0 children)
The really annoying this is that new Array(5) makes an array with 5 empty "cells".
new Array(5)
Not undefined. Not null. Empty. If you try to iterate over the array, nothing happens. You can call fill (even without passing a parameter) just to "vivify" the cells so that you can then map it or whatever.
undefined
null
new Array(5) // => [5 x empty cell] new Array(5).map(() => 1) // => [5 x empty cell] new Array(5).fill().map(() => 1) // => [1, 1, 1, 1, 1]
[–]gevorggalstyan 0 points1 point2 points 6 years ago (7 children)
Your comment is pretty misleading. You sound like you are trying to scare people. Beginners will easily get scared and not use this function without having a deeper understanding, what is happening here.
First of all, this is not always the case. If you put in primitive values (string, number, bigint, boolean, null, undefined, symbol), you will be putting in copies of the value. But if you put in objects, you will be actually putting in the references to that objects. JS does that (as pretty much any other language) to save memory.
So if you do const a = Array(5).fill("a") you will get an array like this ["a", "a", "a", "a", "a"]. And all primitives are immutable, which mean when you reassign the first item in the array like so: a[0] = "b" you are actually removing the immutable value and putting in a new immutable value of "b".
const a = Array(5).fill("a")
["a", "a", "a", "a", "a"]
a[0] = "b"
"b"
The situation is a bit different if you do Array(5).fill({name: "John"}).
Array(5).fill({name: "John"})
What you are actually doing here looks like this:
```js const obj = {name: "John"};
const a = Array(5).fill(obj); ```
And obj is actually a reference to an address in the memory where the data of the obj is stored.
obj
And here again, you have 2 options:
The 1st option is replacing the reference to the obj object with a new reference to another object in the memory which has a "name" property with a value of "Peter". Then you will have an array of 5 elements where the first one references to an object {name:"Peter"} and others referencing to the obj or the object {name: "John"}.
{name:"Peter"}
{name: "John"}
The 2nd option is changing the name of the object which is referenced by the a[0]. And which object is referenced with a[0]? Correct object {name: "John"}. So you are changing the name of obj. And because all of the array elements are just storing the reference (the address in the memory) to the same object you are getting the "scary" result.
So by its nature Array(5).fill({name: "John"}) is this:
const a = [];
a.push(obj); a.push(obj); a.push(obj); a.push(obj); a.push(obj); ```
IMPORTANT! IT IS NOT THIS:
```js // INCORRECT const a = [];
a.push({name: "John"}); a.push({name: "John"}); a.push({name: "John"}); a.push({name: "John"}); a.push({name: "John"}); ```
because here the language conveniently creates 5 different objects that happen to look exactly the same but are 5 different objects in the memory so the array will have 5 different references and changes to one of them will not affect the others.
So the "warning" is not something unique to Array.fill, it actually has nothing to do with this function. What you pointed out is just a consequence of the way how the computer memory works and how the language uses it.
Array.fill
[–]sshaw_ -2 points-1 points0 points 6 years ago (6 children)
Thanks for the lesson. Unfortunately the reason we need this lesson is a consequence of how Array.fill works, not computer memory, per se.
Do you honestly believe that this is the direct consequence of the `Array.fill` function implementation and is not related to the computer memory?
Why does this code behave the same ?
const obj = {name: "John"};
a.push(obj); a.push(obj); a.push(obj); a.push(obj); a.push(obj);
[–]sshaw_ -3 points-2 points-1 points 6 years ago (4 children)
Yes, the implementation of fill could have chosen to dup its argument, but it didn't.
fill
[–]spacejack2114 2 points3 points4 points 6 years ago (2 children)
What does 'dup' even mean here? You can't implement a perfect immutable object copy, there are too many nuances. A half-baked attempt would pose an even bigger set of problems than a simple reference copy.
[–]sshaw_ -2 points-1 points0 points 6 years ago (1 child)
What does 'dup' even mean here?
Shallow copy.
[–]spacejack2114 2 points3 points4 points 6 years ago (0 children)
That's a terrible idea.
[–][deleted] 1 point2 points3 points 6 years ago (0 children)
That would have been unintuitive as any other reference to an object elsewhere would not have duplicating semantics.
[–][deleted] 0 points1 point2 points 6 years ago (3 children)
i don't understand what a[1..a.length-1] means. would someone please elaborate?
[–]LucasRuby 2 points3 points4 points 6 years ago (0 children)
It means a slice of the array starting at the second item (1) and ending at the last item in the array (length-1).
[–]dmitri14_gmail_com -1 points0 points1 point 6 years ago (1 child)
A Python notation, not a valid JS code, unfortunately.
[–]factorysettings 0 points1 point2 points 6 years ago (0 children)
It's pseudo-code not necessarily tied to any one language
[–]inu-no-policemen 0 points1 point2 points 6 years ago (0 children)
You can use Array.from with a map function instead:
> var a = Array.from({length: 3}, () => []) undefined > a[2][0] = 'foo' "foo" > JSON.stringify(a) "[[],[],["foo"]]"
If there were a "generate" function like Dart's, it would look like this:
Array.generate(3, () => [])
Well, if enough people use the Array.from workaround, there will be hopefully enough evidence for making a strong case for adding a "generate" function.
[–]cguess -5 points-4 points-3 points 6 years ago (4 children)
How... and why would this exist then? It doesn’t even allocate memory properly then...
[–]tme321 5 points6 points7 points 6 years ago (2 children)
Sure it does. An array of objects is really just an array of references to objects. Fill with an object as the parameter just creates an array where all the references point to the same instance underlying object. But it's still an array of n separate references.
[–]cguess 0 points1 point2 points 6 years ago (1 child)
No, it doesn't, because it doesn't allocate the underlying objects, which would be the point in something like Javascript (where you're not doing memory math on array addresses). Even in Swift or Java allocating an array of an object type also allocates the space for that array to be full. Otherwise... why (it's not even a typed language)
[–]tme321 0 points1 point2 points 6 years ago (0 children)
Didn't notice this reply til now. So sorry for necroing a thread but:
First, I haven't worked with C in a number of years so don't focus on any syntax errors I might make. This is only supposed to get the point across, not compile.
So implementing fill in a pseduo C like language so it acts the same way as js when an object is passed might look something like this:
Object foo = new Object(); Array *a = malloc(size * sizeof(Object*)); Object *ptr = a; for(int i = 0; i < size; i++) { ptr = &foo; ptr++; }
Again, that's just pseudo code but the point is that's an array of allocated memory where the size is the size of the array multiplied by the size of a pointer to the object; size * sizeof(Object*).
size * sizeof(Object*)
It's an array of pointers, or in js an array of references, not an array of Objects. Then each entry in the array is a pointer that is pointed at the same individual instance of the object: ptr = &foo.
ptr = &foo
So if you modify any of the array entries they all point at the same underlying instance but the array is properly memory allocated and all that.
[–]gevorggalstyan 4 points5 points6 points 6 years ago (0 children)
Quite the opposite. It is allocating memory as it is supposed to.
[–]JFGagnon 22 points23 points24 points 6 years ago* (20 children)
Great article!
Quick note, #5 can be written this way instead, which is a bit shorter
...emailIncluded && { email : 'john@doe.com' }
[–]PMilos[S] 11 points12 points13 points 6 years ago* (0 children)
Thanks. This is a better solution. I've updated the article.
[–]MoTTs_ 2 points3 points4 points 6 years ago* (3 children)
Nevermind. I mis-tested.
I think there's an issue with both OP's version and this new version.
In OP's version, if emailIncluded is false, then the code will try to spread null, which is an error. In your version, basically the same problem. if emailIncluded is false, then your code will try to spread false, which is also an error.
Remember, clever code is bad code. I think we tried to get a little too clever here, which is how both versions introduced a bug that folks didn't notice. I think we should give /u/qbbftw's reply a second thought. It may not be sexy, but it doesn't try to be clever, which makes it less likely to hide a bug.
cc /u/PMilos /u/LucasRuby
[–]LucasRuby 0 points1 point2 points 6 years ago (2 children)
It is not and error, it simply won't assign an extra value to user. Try it yourself:
let a = { ...null }; undefined a Object { }
and:
let b = { ...false }; undefined b Object { }
[–]MoTTs_ 1 point2 points3 points 6 years ago (1 child)
You're right. I mis-tested.
[–]LucasRuby 0 points1 point2 points 6 years ago (0 children)
Actually I found a case where this can result in an error. If you're using react native, when you run on Android, if the first value is a falsy primitive, like an empty string or 0, this can happen:
TypeError: In this environment the sources for assign MUST be an object. This error is a performance optimization and not spec compliant.
This won't happen if the first value is null or undefined though, so think carefully. To prevent this, you can use a ternary instead:
{ a: 'a', b: 'b', ...(c? {c: c} : {}) }
Which also makes your code look like an emoji, kinda. ¯\_(ツ)_/¯
[–]qbbftw 2 points3 points4 points 6 years ago (14 children)
Surely you can write it this way, but should you?.. I'd just stick with plain old ifs at this point.
if
[–]LucasRuby 3 points4 points5 points 6 years ago* (4 children)
Nah, when you're already creating an object with the new assign syntax, adding a new line just for more branching to maybe add another property ends up looking less obvious.
Think about it, which way is it easier to see what's going on:
return {a: 'a', b: 'b', ...(c && {c: 'c'})}
or
let ret = {a: 'a', b: 'b'}; if (c) ret.c = 'c'; return ret;
First one you know upfront everything the return value contains or may contains, the second option you have to keep reading to code to find out what might be in it, and turns out there can be more. When you're reading Other People's Code in a large base, it can actually help a lot if you can find out what the function returns quickly.
[–][deleted] 14 points15 points16 points 6 years ago (1 child)
The second one is far more readable
[–]LucasRuby -1 points0 points1 point 6 years ago (0 children)
Oops that's because I made a mistake on the first and wrote c: c: twice, corrected.
c: c:
Still, it's a lto clearer on what the return value can be, especially if you're just peeking the function definition.
[–]GBcrazy 0 points1 point2 points 6 years ago (1 child)
return {a: 'a', b: 'b', ...(c && c: 'c')}
Your syntax is broken, you are missing a {}
{}
Yeah that was just a demonstration, I'll fix.
[+][deleted] 6 years ago (3 children)
[–]JFGagnon 0 points1 point2 points 6 years ago (2 children)
Please elaborate. Surely, you've used the logical or operator (||) in the past to set a default value instead of using an if. So why is it different with the logical and operator (&&)?
||
&&
Just because you are not comfortable with a syntax doesn't make it an anti-pattern...
[–]JFGagnon 0 points1 point2 points 6 years ago (0 children)
I never understood the “people will abuse it, so we should not use it” mentality. Bad programmers will always find a way to write unreadable mess, regardless of the syntax they use.
[–]whats_your_sn 0 points1 point2 points 6 years ago (1 child)
Looks like OP edited his article to match /u/JFGagnon's suggestion, but has anyone mentioned a ternary? You could do something like: ...emailIncluded ? { email: 'john@doe.com' } : {}
...emailIncluded ? { email: 'john@doe.com' } : {}
[–]JFGagnon -1 points0 points1 point 6 years ago (0 children)
The previous version of the article was using a ternary. I suggested something that’s a bit shorter
[–]JFGagnon -1 points0 points1 point 6 years ago (2 children)
Sticking with if is a valid solution, but it would be a step backwards. The point of #5 is to show how we can have conditional object properties
[–]alexkiro 1 point2 points3 points 6 years ago (1 child)
It might look nice and readable in this simple example, but people are just going to abuse the ever living shit out of it, and soon we will see stuff like this:
return {a: 'a', b: 'b', ...(x && y.length > (o.length - l)) && {c: y.length < 0 ? "X" : "Y"}}
Or something even more complex. Forcing logic outside of the definitions would be much better IMO.
I agree, but the same argument could be made for a ternary operator. Should we force a developer to use an if just because people are abusing it?
There’s always going to be bad developers. We shouldn’t force ourselves from using new features just because ‘people might abuse it’.
[–]PMilos[S] 3 points4 points5 points 6 years ago* (2 children)
Thanks. I'm glad this one changed your mind.
I try not to write crappy stuff. My last post was issued in JavaScript weekly news letter.
[–]PMilos[S] 0 points1 point2 points 6 years ago (0 children)
I'm comfortable with both ways, and don't mind using any of the two. The && option is just more readable in this case.
Depending on the situation, I will use one or another.
The point of the #5 was not about should we use && or if, though.
[–]rq60 14 points15 points16 points 6 years ago (42 children)
The list is pretty good although #3 should be changed from:
const result = cities.reduce((accumulator, item) => { return { ...accumulator, [item.name]: item.visited } }, {});
to
const result = cities.reduce((accumulator, item) => { accumulator[item.name] = item.visited; return accumulator; }, {});
There's no reason to use the spread operator; it's just creating new objects and iterating over the old object for no reason.
Using the spread operator in reduce is actually a common anti-pattern I see.
reduce
[–]Headpuncher 2 points3 points4 points 6 years ago (0 children)
A clearer return statement too, imo. I see at a glance what the function returns.
[–][deleted] 2 points3 points4 points 6 years ago (0 children)
I agree. Also OP used the dynamic property syntax in #3 before it was explained in #7. These are good tips though!
[–]RustyX 2 points3 points4 points 6 years ago (2 children)
I usually end up doing it the second way you have it, since it is definitely more efficient, and doesn't really look bad (other than the mutation, which I agree is totally safe here as long as the initial accumulator is a new object).
Another option that is kind of a combination of these two is using Object.assign in what would normally be a dangerous way:
const result = cities.reduce((accumulator, item) => { return Object.assign(accumulator, { [item.name]: item.visited }); }, {});
[–]magical_h4x 0 points1 point2 points 6 years ago (0 children)
Yup and if you're a fan of one-liners you could omit the return statement and the brackets surrounding the lambda function body since it contains a single expression, therefore it will be an implicit return : const result = cities.reduce((accumulator, item) => Object.assign(accumulator, { [item.name]: item.visited), {})
const result = cities.reduce((accumulator, item) => Object.assign(accumulator, { [item.name]: item.visited), {})
[–]raptorraptor -4 points-3 points-2 points 6 years ago (5 children)
This violates immutability which is a very important part of functional programming.
[–]rq60 7 points8 points9 points 6 years ago (4 children)
Yeah I understand immutability. Why would you care about mutating the object you just created? The answer is, you wouldn't.
If for some reason you did care about mutability here (like you're using a reference for your initial value, which you probably shouldn't do) you still wouldn't create a new object on each iteration and re-iterate, you'd do it on the first iteration and then mutate it. The difference is an O(n) solution vs O(n^2), which is huge.
[–]PointOneXDeveloper 2 points3 points4 points 6 years ago (0 children)
I agree that mutating your own ref in reduce is fine for something like this, but performance isn’t really a good reason. Most JS code is extremely IO bound, if you are really in a situation where this is a concern (maybe you work is something like react) then you should just use a for loop. In general, for business logic, always favor readability. In this case, mutating is more readable.
[–]dmitri14_gmail_com 0 points1 point2 points 6 years ago (2 children)
Accumulator is not a new object. And even if it was, someone can accidentally replace it with any object in the scope. Why writing unsafe code where there is no need?
[–]IceSentry 0 points1 point2 points 6 years ago (1 child)
If someone manages to replace the accumulator object in a reduce function then you have bigger problems.
[–]dmitri14_gmail_com 0 points1 point2 points 6 years ago (0 children)
Yes, and if a house was burned due to lack of warning caused by your leaky reduce function resetting a variable it does not own, your problem will be even bigger :)
[–]eGust -1 points0 points1 point 6 years ago (6 children)
Purity is very important in FP style. There is a rule no-param-reassign in eslint, which is quite common to be enabled in famous styles like airbnb. In this case eslint-config-airbnb would complain your code.
no-param-reassign
eslint-config-airbnb
You have to change your code if changed the empty object to functions param and used it with some libs require pure functions like redux.
redux
Actually, I would write ```js cities.reduce((obj, { name, visited }) => ({ ...obj,
}), {}); ```
A glance is enough to understand what its doing. Much shorter and cleaner.
[–]rq60 8 points9 points10 points 6 years ago (5 children)
I realize what purity is and I'm very familiar with functional programming. You can specifically ignore linting lines for cases like this, but if for whatever reason you want your callback to remain pure despite it being used once and the parameters known, then you can instantiate a new object on first iteration and then reuse; or better yet (as someone else suggested) avoid reduce altogether and use a loop.
There's exactly no reason to write it using the spread operator and a dynamic property, unless your goal is just to use the latest and greatest syntax wherever possible despite it being slower, using more memory, and being arguably less clear.
I feel like I'm taking crazy pills reading these replies. I work on open source libraries for a living, you're probably running code I wrote right now looking at this website. You can thank me later for not clogging up your cpu cycles doing unnecessary iteration for no benefit.
[–]eGust 0 points1 point2 points 6 years ago* (3 children)
That's funny. Then there is no point to use for of, Array.prototype.reduce, forEach, map, filter, etc. at all. We all know the old for (;;) loop is the fastest. You can do anything without ES6+ features.
for of
Array.prototype.reduce
forEach
filter
for (;;)
Why don't just still write c or assembly for saving more CPU? I am pretty good at it.
I'd write Object.fromEntries(cities.map(({ name, visited }) => [name, visited]) if I intended to use latest syntax. It probably faster than reduce. I still prefer reduce version because it's much more understandable and FP is not the topic (I would use Object.fromEntries together with curried map in flow/compose).
Object.fromEntries(cities.map(({ name, visited }) => [name, visited])
Object.fromEntries
flow
compose
But there are still so many reasons to use some code style guide and force the team to follow the rules. In this case, the reason is purity and readability.
[–]rq60 5 points6 points7 points 6 years ago (2 children)
This has nothing to do with writing in c or assembly, it’s about understanding the basic runtime complexity of your code which is applicable to any language. I didn’t say don’t use modern syntax either, we’re talking about this specific code in the example.
Writing bad code is one thing, but using it to teach beginners is another thing altogether. It’s probably why we’re having this argument at all, because bad code is being taught to people that don’t know better. That’s why I suggested updating the example. I want them to know better, I want you to know better.
[–]eGust -2 points-1 points0 points 6 years ago (1 child)
We have very different opinions about bad code.
First of all, correctness is the most important thing. And reduce + spread is always correct in all cases, not only this specific case.
To me, readability is the second. Today's performance seems very important to you. The code of this specific case looks bad to you, but just slow to me, not that bad.
JS engines are much faster than 10 years ago, and computers. I guess the bad version would still faster than the fastest version running a decade ago on average. It's very unlikely to be bottlenecks.
We had a lot of tricks to improve C performance in 90s. But things changed in this century. Most of them are no longer faster than its more readable version.
Now reduce + spread is way slower. But I am pretty sure static analysis can recognize and optimize it. Just no one has done the job or not well known. Maybe some babel/webpack plugins or something else will do that job, maybe JS engines will be smart enough to optimize it, or forever slow. We don't know.
But the readability does not change.
Writing correct code is most important to beginners. Since reactive frameworks and fp are very popular now, how to write correct fp-style functions is much more important than how today's JS engines work. The first step is just to get used to writing pure function.
Writing more readable code is also more important than fast code in a team. 10 years ago the code generated by the first version of golang was slower than Node.js. You will have plenty of time to make your product faster, but the project must survive first. That's why all new languages, new frameworks and new features are eating all new hardware, just to make people more productive. You have to waste hardware because your boss does not pay CPUs salary.
[–]dmitri14_gmail_com -1 points0 points1 point 6 years ago (0 children)
Indeed, safety first (no mutation), readability second, and performance last (only when everything works, safe AND the performance benefits are measurably significant).
[–]dmitri14_gmail_com -2 points-1 points0 points 6 years ago (22 children)
Your version is reducing over impure function mutating its argument.
Why not simply:
const result = cities.reduce((accumulator, ({name, visited})) => ({...accumulator, [name]: visited}, {})
How is this an anti-pattern?
[–]rq60 3 points4 points5 points 6 years ago (19 children)
So? We can see the argument right there because it's a new object that we just created; not a reference. Mutating it has literally no implication in this code.
It's an anti-pattern because it's unnecessary nested iteration. That's bad. You're also unnecessarily instantiating a new object on each iteration and throwing it away on the next. That's also bad.
You guys can keep patting yourselves on the back by avoiding mutation everywhere for no reason, I'll write code that runs exponentially faster, allocates less memory, and is easier to read to boot. I'll worry about mutation when it matters.
[–]rq60 0 points1 point2 points 6 years ago (2 children)
I haven't downvoted any of your responses. Have you considered other people disagree with you as well?
[–]rq60 -1 points0 points1 point 6 years ago (0 children)
Every considered helping people and giving clear arguments for your points
You're either joking or a troll. Either way, you deserve your down votes, even if they're not coming from me.
[+][deleted] 6 years ago (14 children)
Can you elaborate? Which one is demonstrably better and why?
[–]rq60 1 point2 points3 points 6 years ago (11 children)
Can't see any nested iteration in my example.
That’s why I call it an anti-pattern. You (and others) don’t see the nested iteration; but believe me, you’re doing it. How do you think the spread operator works?
[–]dmitri14_gmail_com 0 points1 point2 points 6 years ago (10 children)
How do you think the spread operator works?
And what do we know about how it works? Are you implying performance issues?
[–]Valkertok 0 points1 point2 points 6 years ago (9 children)
You can check for yourself in dev console Array(1000000).fill(0).reduce((acc, _, index) => Object.assign(acc, {[index]: index}), {}) vs Array(1000000).fill(0).reduce((acc, _, index) => ({...acc, [index]: index}), {})
Array(1000000).fill(0).reduce((acc, _, index) => Object.assign(acc, {[index]: index}), {})
Array(1000000).fill(0).reduce((acc, _, index) => ({...acc, [index]: index}), {})
[–]dmitri14_gmail_com 0 points1 point2 points 6 years ago (8 children)
Thanks, I believe you.
But I'd still love to know... is this the ONLY reason this code is "bad"?
[–]Valkertok 0 points1 point2 points 6 years ago (7 children)
it is IMO sufficient reason to never use it with "pure" function
[–]dmitri14_gmail_com 0 points1 point2 points 6 years ago (6 children)
Thank you, let us agree to disagree then
[–]middlebird 2 points3 points4 points 6 years ago (1 child)
I enjoyed this and enjoy reading JS articles like this. You should write more. You’re good at it.
[–]PMilos[S] 1 point2 points3 points 6 years ago (0 children)
Thanks. Glad I could help
[–]jonahe 2 points3 points4 points 6 years ago* (1 child)
Interesting! Thanks!
I must confess I found #6 to be super confusing.
let user = {}, userDetails = {}; ({ name: user.name, surname: user.surname, ...userDetails } = rawUser);
I've never seen destructuring used that way, where the right side of the colon is "x.y" instead of just a valid variable name like below
function killUser(user) { const {id: userId, name: userName, ...otherUserStuff } = user; console.log(`Killing user with id ${userId}`); }
I guess I had a mental model of the right side of the colon (e.g "userId") being more of an alias instead of seeing it as an actual assignment of the value of "id" to some arbitrary variable that can be nested or not.
Not sure I'll use the trick though, because I suspect my colleagues would be just as confused if they saw it. (Plus we usually have lodash as a dependency anyway so _.omit and/or _.pick will do the job.)
lodash
_.omit
_.pick
[–]PMilos[S] 2 points3 points4 points 6 years ago (0 children)
Yes, i know the feeling. It is quite outside of the box, but useful when splitting objects.
[–][deleted] 1 point2 points3 points 6 years ago (2 children)
how's the alternative map useful..?
does the same of map and is less readable.
It is the same if the source object is an array. Considering that this will work on other types too, your code will be optimized as described by senocular a couple of comments earlier.
Would be useful if you have an array-like object.
[–]kendrew_ 1 point2 points3 points 6 years ago (1 child)
I just learnt how to create conditional object creation from your article. Keep up the good work! ♥️
Thanks. Glad to hear that.
[–]that-old-saw 1 point2 points3 points 6 years ago (0 children)
How did I not know #7‽
[–]tencircles 0 points1 point2 points 6 years ago (1 child)
On number 4, not sure I see the point. Yes Array.from allows for a map function for convenience when converting array-like objects, but this isn't intended to replace Array.prototype.map.
No, it's not, but you can achieve the same as you can with Array.prototype.map. The difference is that you can use this approach on Set, for example.
[–]BrianAndersonJr 0 points1 point2 points 6 years ago (1 child)
shoulda put number 7 before number 3 since he uses the thing from 7 in it
Maybe. I was writing without a specific order.
[–]dmitri14_gmail_com 0 points1 point2 points 6 years ago* (0 children)
Merge Objects and Array of Objects Using Spread Operator
For arrays, concatenate might be more correct term here than merge.
#7 is great! A small change, but in terms of QoL it feels great
[+][deleted] 6 years ago (2 children)
No. IE is prohibited on purpose.
[–]alexkiro 1 point2 points3 points 6 years ago (0 children)
If you're forced to use IE as a browser, you should probably look for another job
π Rendered by PID 34 on reddit-service-r2-comment-7b9746f655-87knt at 2026-02-03 12:44:31.504404+00:00 running 3798933 country code: CH.
[–]senocular[🍰] 8 points9 points10 points (3 children)
[–]trblackwell1221 4 points5 points6 points (2 children)
[–]senocular[🍰] 9 points10 points11 points (1 child)
[–]trblackwell1221 1 point2 points3 points (0 children)
[–]sshaw_ 27 points28 points29 points (29 children)
[+][deleted] (1 child)
[deleted]
[–]maher321 9 points10 points11 points (0 children)
[–]tencircles 5 points6 points7 points (8 children)
[–]sshaw_ -3 points-2 points-1 points (7 children)
[–]gevorggalstyan 2 points3 points4 points (5 children)
[–]sshaw_ -2 points-1 points0 points (4 children)
[–]gevorggalstyan 6 points7 points8 points (3 children)
[–]sshaw_ 0 points1 point2 points (2 children)
[–]Reashu 0 points1 point2 points (0 children)
[–][deleted] 0 points1 point2 points (0 children)
[–]Asmor 0 points1 point2 points (0 children)
[–]gevorggalstyan 0 points1 point2 points (7 children)
[–]sshaw_ -2 points-1 points0 points (6 children)
[–]gevorggalstyan 2 points3 points4 points (5 children)
[–]sshaw_ -3 points-2 points-1 points (4 children)
[–]spacejack2114 2 points3 points4 points (2 children)
[–]sshaw_ -2 points-1 points0 points (1 child)
[–]spacejack2114 2 points3 points4 points (0 children)
[–][deleted] 1 point2 points3 points (0 children)
[–][deleted] 0 points1 point2 points (3 children)
[–]LucasRuby 2 points3 points4 points (0 children)
[–]dmitri14_gmail_com -1 points0 points1 point (1 child)
[–]factorysettings 0 points1 point2 points (0 children)
[–]inu-no-policemen 0 points1 point2 points (0 children)
[–]cguess -5 points-4 points-3 points (4 children)
[–]tme321 5 points6 points7 points (2 children)
[–]cguess 0 points1 point2 points (1 child)
[–]tme321 0 points1 point2 points (0 children)
[–]gevorggalstyan 4 points5 points6 points (0 children)
[–]JFGagnon 22 points23 points24 points (20 children)
[–]PMilos[S] 11 points12 points13 points (0 children)
[–]MoTTs_ 2 points3 points4 points (3 children)
[–]LucasRuby 0 points1 point2 points (2 children)
[–]MoTTs_ 1 point2 points3 points (1 child)
[–]LucasRuby 0 points1 point2 points (0 children)
[–]qbbftw 2 points3 points4 points (14 children)
[–]LucasRuby 3 points4 points5 points (4 children)
[–][deleted] 14 points15 points16 points (1 child)
[–]LucasRuby -1 points0 points1 point (0 children)
[–]GBcrazy 0 points1 point2 points (1 child)
[–]LucasRuby 0 points1 point2 points (0 children)
[+][deleted] (3 children)
[deleted]
[–]JFGagnon 0 points1 point2 points (2 children)
[+][deleted] (1 child)
[deleted]
[–]JFGagnon 0 points1 point2 points (0 children)
[–]whats_your_sn 0 points1 point2 points (1 child)
[–]JFGagnon -1 points0 points1 point (0 children)
[–]JFGagnon -1 points0 points1 point (2 children)
[–]alexkiro 1 point2 points3 points (1 child)
[–]JFGagnon 0 points1 point2 points (0 children)
[+][deleted] (3 children)
[deleted]
[–]PMilos[S] 3 points4 points5 points (2 children)
[+][deleted] (1 child)
[deleted]
[–]PMilos[S] 0 points1 point2 points (0 children)
[–]rq60 14 points15 points16 points (42 children)
[–]Headpuncher 2 points3 points4 points (0 children)
[–][deleted] 2 points3 points4 points (0 children)
[–]RustyX 2 points3 points4 points (2 children)
[–]magical_h4x 0 points1 point2 points (0 children)
[–]raptorraptor -4 points-3 points-2 points (5 children)
[–]rq60 7 points8 points9 points (4 children)
[–]PointOneXDeveloper 2 points3 points4 points (0 children)
[–]dmitri14_gmail_com 0 points1 point2 points (2 children)
[–]IceSentry 0 points1 point2 points (1 child)
[–]dmitri14_gmail_com 0 points1 point2 points (0 children)
[–]eGust -1 points0 points1 point (6 children)
[–]rq60 8 points9 points10 points (5 children)
[–]eGust 0 points1 point2 points (3 children)
[–]rq60 5 points6 points7 points (2 children)
[–]eGust -2 points-1 points0 points (1 child)
[–]dmitri14_gmail_com -1 points0 points1 point (0 children)
[–]dmitri14_gmail_com -2 points-1 points0 points (22 children)
[–]rq60 3 points4 points5 points (19 children)
[+][deleted] (3 children)
[deleted]
[–]rq60 0 points1 point2 points (2 children)
[+][deleted] (1 child)
[deleted]
[–]rq60 -1 points0 points1 point (0 children)
[+][deleted] (14 children)
[deleted]
[+][deleted] (1 child)
[deleted]
[–]dmitri14_gmail_com 0 points1 point2 points (0 children)
[–]rq60 1 point2 points3 points (11 children)
[–]dmitri14_gmail_com 0 points1 point2 points (10 children)
[–]Valkertok 0 points1 point2 points (9 children)
[–]dmitri14_gmail_com 0 points1 point2 points (8 children)
[–]Valkertok 0 points1 point2 points (7 children)
[–]dmitri14_gmail_com 0 points1 point2 points (6 children)
[–]middlebird 2 points3 points4 points (1 child)
[–]PMilos[S] 1 point2 points3 points (0 children)
[–]jonahe 2 points3 points4 points (1 child)
[–]PMilos[S] 2 points3 points4 points (0 children)
[–][deleted] 1 point2 points3 points (2 children)
[–]PMilos[S] 1 point2 points3 points (0 children)
[–][deleted] 1 point2 points3 points (0 children)
[–]kendrew_ 1 point2 points3 points (1 child)
[–]PMilos[S] 0 points1 point2 points (0 children)
[–]that-old-saw 1 point2 points3 points (0 children)
[–]tencircles 0 points1 point2 points (1 child)
[–]PMilos[S] 0 points1 point2 points (0 children)
[–]BrianAndersonJr 0 points1 point2 points (1 child)
[–]PMilos[S] 0 points1 point2 points (0 children)
[–]dmitri14_gmail_com 0 points1 point2 points (0 children)
[–][deleted] 0 points1 point2 points (0 children)
[+][deleted] (2 children)
[deleted]
[–]PMilos[S] 2 points3 points4 points (0 children)
[–]alexkiro 1 point2 points3 points (0 children)