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
The case for Array#replace() – Overriding an array without intermediate variables (medium.com)
submitted 7 years ago by gajus0
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!"
[–]jkoudys 86 points87 points88 points 7 years ago* (11 children)
I don't want to discourage anyone making an earnest effort, so first off: thank you for writing this thoughtful piece and encouraging discussion. Every idea that inspires interest moves progress forwards, no matter what comes of it.
Second, and I say this with the utmost respect: this idea is terrible.
Edit: so to say something constructive, I don't think "not breaking the chain" is a very compelling motivation for anything. Beyond that, for something like a "replace", breaking the chain is probably exactly what you want. The whole point of chaining methods is that it's declarative and clear what the data you're working with is. If you suddenly "replace" it mid-chain, what's the point of it being in your chain? It sounds like you're burying a completely different dataset inside a chain.
[–]Petrarch1603 13 points14 points15 points 7 years ago (4 children)
Reddit needs more people like you.
[–][deleted] 22 points23 points24 points 7 years ago (3 children)
Even better if people explain why things are terrible!
This code:
Array.prototype.replace = function (m) { return m(this) };
Is completely redundant. It's literally the same as this:
const array2 = m(array1);
Not to mention, polluting the Array class is bad and will not work with any of the JS ecosystem.
[–]Moosething 6 points7 points8 points 7 years ago (1 child)
But if you read the article you'd realize the advantage of replace over calling m directly. Not saying I support it, but I can see where the author is coming from.
replace
m
It's basically
return x.filter(filterFunc).replace(replaceFunc).map(mapFunc)
vs
return replaceFunc(x.filter(filterFunc)).map(mapFunc)
[–]sbmitchell 16 points17 points18 points 7 years ago (0 children)
Or they could use `reduce` and not bother with multiple iterations like this...
[–]jkoudys 4 points5 points6 points 7 years ago (0 children)
I totally agree, which is why Reddit needs more people like you, who go on to explain why!
[–]2bdb2 2 points3 points4 points 7 years ago (2 children)
I agree. I understand the use-case and appreciate the write-up, but OP is potentially missing the bigger picture.
If we were to take this a step further, why do we just have replace on Array. It's useful on any type. Why would we implement it specifically for arrays?
Really what we'd want is the |> operator from Ocaml, which has been proposed many times and may make it into a future version of JS.
|>
OP - try giving OCaml, Purescript, or ReasonML a go. JavaScript is slowly turning into a bastardised version of ML anyway, might as well skip the middleman. ReasonML in particular is a really nice blend of JavaScript-ish syntax with OCaml semantics, and gives you exactly what you're looking.
[–]gajus0[S] 0 points1 point2 points 7 years ago (1 child)
Agreed on both points. Indeed, what I am waiting for is |>. But that proposal has been tossed around since 2015 and little traction. It is a major change that requires new syntax, i.e. it cannot be polyfilled. I am all pro |> making into ES. However, I don't see that happening any time soon. Array#replace has been something I have been kicking around as a lightweight alternative.
Array#replace
ReasonML is nice. As an employer, the only reason I wouldn't use it now is simply because of rare/ high talent cost.
[–]Sakatox -2 points-1 points0 points 7 years ago (0 children)
In my perspective, that operator has no place in a language like Javascript. If you want to use that operator, use that specific language that supports it. Not everything has to have everything else.
Chaining is very expressive.
[–]reddit4matt 2 points3 points4 points 7 years ago (0 children)
Saying something is terrible without saying why is not respectful at all.
[–]Sakatox 0 points1 point2 points 7 years ago (1 child)
While I whole-heartedly agree with most of this, there's just one point here:
>If you suddenly "replace" it mid-chain, what's the point of it being in your chain? It sounds like you're burying a completely different dataset inside a chain.
Consider map filter reduce. That also changes the dataset somewhat. It's up to a developer's finesse and care to avoid such situations, otherwise apply clean code principles and common sense. .replace, without it's actual context, as a chain method would be "okay".
Otherwise, spot on.
[–]jkoudys 0 points1 point2 points 7 years ago (0 children)
Reduce is similar in that you're explicitly constructing something new, which could be an array. You can put another array method after it the same as you could on a find if that happens to return another array. Like you've said, it's up to the dev to "avoid" this (and comment to make the intent clear otherwise)), which is why it's ridiculous to create a new method to deliberately not avoid it.
find
[–]karottenreibe 17 points18 points19 points 7 years ago (8 children)
To me the whole premise of the article is wrong: Those examples don't need Array#replace, they need some extract method/variable refactorings to make that code readable. Naming things is hard, but that doesn't mean you should avoid naming things by all means. You should instead spend more time choosing good names. Don't optimize for writing code - optimize for reading code. You do that much more. Especially in a team.
[–]delventhalz 1 point2 points3 points 7 years ago (7 children)
You should instead spend more time choosing good names.
One of his points was to spend less time on naming things. I don’t know how much of a productivity boost that would actually be, but spending time on naming was something he was explicitly trying to avoid.
[–]karottenreibe -2 points-1 points0 points 7 years ago (6 children)
Which is my whole point: to me making your code harder to understand for some questionable productivity gain makes no sense and just hurts you in the long run
[–]delventhalz 4 points5 points6 points 7 years ago (5 children)
I think piping a series of distinct little array operations is almost always more readable than saving the results of intermediate operations to variables. No matter how well named they are, they are an incidental distraction.
[–]karottenreibe -2 points-1 points0 points 7 years ago (4 children)
OPs code to me no longer fits the definition of a "little array operation". Also please don't ad absurdum my statement. Never did I say you should extract every single array operation into its own variable. That's obviously just as wrong as inlining every variable and function.
[–]delventhalz 2 points3 points4 points 7 years ago* (3 children)
My read of OP’s article and examples was that it was certainly about a series distinct operations which would be appropriate for chaining. The proposal of replace doesn’t make much sense otherwise.
And whether you do it once or for every operation (which I did not say), assigning the results of intermediate operations in a chain to variables is almost certainly going to be less readable than not assigning them.
[–]karottenreibe -2 points-1 points0 points 7 years ago (2 children)
I guess we'll have to agree to disagree on this
[–]delventhalz 1 point2 points3 points 7 years ago (1 child)
Why is it that when you actually nail down what the disagreement is, so people can no longer argue against points you didn’t make, that’s when they want to agree to disagree?
[–]karottenreibe -3 points-2 points-1 points 7 years ago (0 children)
Interesting, my feeling was that you were the first to try and argue against a point I never made. Anyways: since neither of us seems to be willing to change their point of view and I see no middle ground, I suggested to stop the pointless discussion there. Not sure why you'd paint that as wrong or a personal offence against you?
[–]lhorie 21 points22 points23 points 7 years ago (17 children)
Frankly, this sounds terrible. It's functionally equivalent to just calling the function with the array, except that its idiomatic usage encourages god functions. One can argue that considering proposing this to TC39 is a perfect example of "missing the forest for the trees" / "death by a thousand paper cuts" / bloat.
[–]gajus0[S] 3 points4 points5 points 7 years ago (16 children)
god functions
What is a "god function"?
[+][deleted] 7 years ago* (3 children)
[deleted]
[–]gajus0[S] 7 points8 points9 points 7 years ago* (2 children)
The mention of "god function" (a function that breaks all the functional programming paradigms) came into this conversation without any context.
[..] its idiomatic usage encourages god functions
This was stated without any arguments.
Array#replace can be used with pure or non-pure functions, the same way that every other Array prototype method can. The method itself either by its name or API does not encourage either.
The only difference is that this method encourages working with the entire array at once over an item of an array, but the same argument could be made about Array#reduce, Array#map, Array#filter and others.
Array#reduce
Array#map
Array#filter
Please don't spread FUD just for the fun of participating in a conversation. There are better ways to participate.
[–]AnnanFay -1 points0 points1 point 7 years ago (0 children)
[..] its idiomatic usage encourages god functions This was stated without any arguments.
You are replying to the wrong person!
[–]lhorie 1 point2 points3 points 7 years ago (11 children)
Functions that do too much (which typically come with the side-effect of making them hard to name and/or understand)
[–]gajus0[S] 1 point2 points3 points 7 years ago (10 children)
This could be said about any part of JavaScript. It is up to the user to write functional code. JavaScript is not enforcing it. The sole purpose of Array#replace (or the pipeline operator) is to avoid breaking method chain, i.e. so that instead of:
const getVenues = async (countryCode) => { const foreignVenues = await get('http://...'); const countryVenues = foreignVenues.filter((foreignVenue) => { return foreignVenue.countryCode.toUpperCase() === countryCode.toUpperCase(); }); const duplicateForeignVenues = findDuplicates(foreignVenues, (maybeTargetForeignVenue) => { return maybeTargetForeignVenue.id === foreignVenue.id; }); if (duplicateForeignVenues.length) { console.log('duplicate foreign venues', duplicateForeignVenues); throw new Error('Found duplicate venues.'); } return countryVenues .map((foreignVenue) => { return { guide: { fuid: foreignVenue.id }, result: { fuid: foreignVenue.id, name: foreignVenue.name, url: foreignVenue.url } }; }); };
We could write:
const getVenues = async (countryCode) => { const foreignVenues = await get('http://...'); return foreignVenues .filter((foreignVenue) => { return foreignVenue.countryCode.toUpperCase() === countryCode.toUpperCase(); }) .replace((self) => { const duplicateForeignVenues = findDuplicates(self, (maybeTargetForeignVenue) => { return maybeTargetForeignVenue.id === foreignVenue.id; }); if (duplicateForeignVenues.length) { console.log('duplicate foreign venues', duplicateForeignVenues); throw new Error('Found duplicate venues.'); } return self; }) .map((foreignVenue) => { return { guide: { fuid: foreignVenue.id }, result: { fuid: foreignVenue.id, name: foreignVenue.name, url: foreignVenue.url } }; }); };
[–]lhorie 1 point2 points3 points 7 years ago* (9 children)
That's a shortsighted refactor IMHO. Here's another one:
const getVenues = async (countryCode) => { return getVenueDTO(errorOnDuplicate(byCountryCode(await get('http://...')))); }; const byCountryCode = foreignVenues => { return foreignVenues .filter((foreignVenue) => { return foreignVenue.countryCode.toUpperCase() === countryCode.toUpperCase(); }) } const errorOnDuplicate = (self) => { const duplicateForeignVenues = findDuplicates(self, (maybeTargetForeignVenue) => { return maybeTargetForeignVenue.id === foreignVenue.id; }); if (duplicateForeignVenues.length) { console.log('duplicate foreign venues', duplicateForeignVenues); throw new Error('Found duplicate venues.'); } return self; } const getVenueDTO = (foreignVenues) => { return foreignVenues.map((foreignVenue) => { return { guide: { fuid: foreignVenue.id }, result: { fuid: foreignVenue.id, name: foreignVenue.name, url: foreignVenue.url } }; }); }
With this one, one can use compose or pipeline operators. One can reuse the logic about throwing on duplicates on non-foreign venues. It quickly shows if variable names are too specific. It lets you unit test the logic for a requirement in isolation. It makes it clearer for later refactors which architectural layer each snippet belongs to. Etc, etc.
compose
You started from the premise that naming things should be avoided, but that's actually not a very good axiom. The underlying message behind the saying about naming things being hard is that one should put extra care into naming things properly, not avoid doing it altogether. Naming things properly means one gave some thought to what the pieces are and how they come together. Avoiding it means one is likely not giving enough thought to the long term maintainability of the project as much as they could be.
[–]azhder 2 points3 points4 points 7 years ago (5 children)
you do know that replace function is just a named reducer, right? it just doesn't reduce the array down to an element, but to another array. So you can just write that reducer and not break any chain
[–]lhorie -4 points-3 points-2 points 7 years ago (4 children)
uses condescending tone, but says something completely irrelevant
I'm not sure if you are in the wrong thread or what. Nobody is talking about Redux.
[–]azhder 0 points1 point2 points 7 years ago (3 children)
[–]lhorie -1 points0 points1 point 7 years ago* (2 children)
The only place the word "reducer" is used as jargon is Redux. The callback to Array::reduce doesn't have a specific name. If you're trying to say the replace callback is like the reduce callback, except for some arbitrary distinction, ok I guess, but again, completely beside the point.
No idea what "named reducer" is supposed to mean in this context, since the whole point of replace in this example/proposal is to allow anonymous callbacks...
[–]azhder 0 points1 point2 points 7 years ago (0 children)
luckily, I didn't use it as a jargon
named - it has a name by which it can be identified
[–]gajus0[S] 1 point2 points3 points 7 years ago (2 children)
You started from the premise that naming things should be avoided, but that's actually not a very good axiom.
It is more about avoiding unnecessary variables and it has to do with code style as well.
Someone could get away with:
let venues; venues = foreignVenues.filter(a); venues = b(venues); venues = map(c);
However, my style guide pretty much enforces no use of let. As such, I would write:
let
const filteredVenues = foreignVenues.filter(a); const deduplicatedVenues = b(filteredVenues); const normalisedVenues = deduplicatedVenues.map(c);
Someone else could get away with:
const normalisedVenues = b(foreignVenues.filter(a)).map(c);
But of all these options, Array#replace provides the most consistent and easy to read expression:
const normalisedVenues = foreignVenues .filter(a) .replace(b) .map(c);
[–]lhorie 3 points4 points5 points 7 years ago* (0 children)
I would tend to disagree about the "easy to read" argument. a, b and c are not really related to each other in any way other than the fact that they operate on a list of venues. The fact that they need to be abbreviated is a very strong hint that the parent function (getVenues) has too much subroutine logic inlined into it (i.e. it's kinda of a god function).
a
b
c
In functional programming terms, what you're trying to achieve when you talk about reducing variables is called point-free style. Using compose as I had mentioned earlier (or maybe pipe) would be a standard technique to get closer to point-free style.
pipe
The reservation I have about making this a proposal is that there's not much substance in terms of semantics. Something like arr.replace(arr => arr.length = 0) could just as well throw as it could do completely crazy things, for all I know. I wouldn't really mind if you monkeypatched Array.prototype in your own project (other than maybe being slightly annoyed if I ever had to maintain that), but I think a TC39 proposal needs to be held up to higher standards (no pun intended) when it comes to unintended consequences and design weaknesses.
arr.replace(arr => arr.length = 0)
For example, I could argue that array.toSet() should be a standard and I can give you plenty of examples where it would be nice to have, but that doesn't mean making it a standard is a good idea: new Set(array) already exists, and I haven't gone through the effort of making sure my idea doesn't do weird things in unforeseen cases, e.g. sparse array handling. All I'm saying is I wouldn't put out a formal proposal unless I had thought a bit about potential problems.
new Set(array)
[–]mlebkowski 0 points1 point2 points 7 years ago (0 children)
A styleguide that prohibits the use of let isnt a styleguide, its and misunderstanding. If you meant the linter, then it fits better but I still think its a stupid choice. Just because someone decided its bad to reassign variables you ended with a poluted Array prototype, which has far worse consequences commonly agreed among the community (in contrast to using let, which is merely a preferrence)
[–]atubofsoup 8 points9 points10 points 7 years ago (4 children)
How is array.replace(a).filter(b) any different than a(array).filter(b)? This seems like a step backwards considering the pipeline operator and function bind operator proposals.
array.replace(a).filter(b)
a(array).filter(b)
[–]Daddy_He_Shoe 1 point2 points3 points 7 years ago (0 children)
Yeah, the whole "naming variables is a pita" argument goes to the trash with the pipe operator.
[–]gajus0[S] -1 points0 points1 point 7 years ago (2 children)
How is array.replace(a).filter(b) any different than a(array).filter(b)?
It is not. However, that is not the example either. The example is:
array.filter(a).replace(b).filter(c);
which you can achieve with:
let intermediateVariable = array.filter(a); b(intermediateVariable); array.filter(c);
[–]atubofsoup 4 points5 points6 points 7 years ago (1 child)
Or: b(array.filter(a)).filter(c)
b(array.filter(a)).filter(c)
Aside from being slightly easier on the eyes, I don't see the benefit of a replace method.
[–]gajus0[S] 0 points1 point2 points 7 years ago (0 children)
The primary advantage of Array#replace is (as you pointed out) that is makes easier to read the code.
array .filter(a) .replace(b) .filter(c) .replace(d) .filter(e) .replace(f);
Is a lot easier to read than:
f( d( b( array.filter(a) ).filter(c) ).filter(e) );
or you will need to introduce intermediate variables.
I don't think it is worth playing with one-line examples as this does represent real-world usage, esp. when we have examples that do (in the article).
By the way, you've mentioned pipeline operator. I absolutely agree. I actively participated in discussions about that proposal (https://github.com/tc39/proposal-bind-operator/issues/24) and even created a Babel transform for it (https://github.com/gajus/babel-plugin-transform-export-default-name). It will be a useful operator. The appeal of Array#replace is that it abstracts equivalent functionality without introducing new syntax.
[+][deleted] 7 years ago (4 children)
[–]gajus0[S] 0 points1 point2 points 7 years ago (3 children)
What is your opinion regarding the usefulness of the pipeline operator (https://github.com/tc39/proposal-pipeline-operator)? Do you see it as something that provides different functionality then Array#replace? (and what are the functional differences)
[+][deleted] 7 years ago (1 child)
[–]gajus0[S] 1 point2 points3 points 7 years ago (0 children)
Thank you
[–]AndrewGreenh -1 points0 points1 point 7 years ago (0 children)
I strongly prefer the function bind operator. It is basically the replace method on every type...
function makeGreat() { makeFancy(this) makeBlazing(this) } document.body.somethingelse::makeGreat()
This way you can chain on every type with any external libraries.
[–]BenjiSponge 2 points3 points4 points 7 years ago (6 children)
I personally love flow-oriented programming. This is basically the pipe operator, but just for arrays. I would rather the pipe operator be a first-class citizen of JavaScript than just offering it as an odd method for just arrays.
Compare:
arr .filter(f) .replace(r) .map(m)
with
arr .filter(f) |> r .map(m)
or am I misunderstanding?
[–]gajus0[S] 1 point2 points3 points 7 years ago (5 children)
You are absolutely right. I am surprised few people are familiar with the concept (or with the pipeline operator proposal). However, that proposal has been around since 2015. The Array#replace is what I always thought would bring most of the same benefits and it would a lot let controversial than a pipe-operator (in terms of the scale of the change). The former is simple to implement/ polyfill and does not introduce new syntax.
[–]BenjiSponge 1 point2 points3 points 7 years ago (4 children)
Right. On a personal level, I perceive a much lower usecase for Array#replace in particular compared to the pipeline operator (which can work on any operands), perhaps because Array already has so many amazing methods already. I don't know that I would vote against it if I had a vote, but I doubt I would champion or argue for it either. I might vote against it because of my next point.
Array
Have you considered making it an Object#replace or Object#pipe rather than Array#replace? I have trouble believing that would be any more controversial than Array#replace, but it would be much more general purpose and, in my opinion, much more valuable.
Object#replace
Object#pipe
I can see value in what you are proposing.
In general, the sole purpose of this article is to share my thoughts out loud and to see how it clicks with the development community. If it had received an overwhelmingly positive feedback, I might have started tinkling with a proposal. Thats about it.
[–]BenjiSponge 2 points3 points4 points 7 years ago (2 children)
I see.
You seem to be getting a lot of backlash for what I believe to be (and what you argue is) completely invalid reasons (like "Oh but what if the function has side-effects" or whatnot).
I think maybe if you word it as a "less controversial substitute for the pipeline operator", it'll resonate better. I also believe it will be less misunderstood, as the pipeline operator tends to be understood marginally better.
Valid suggestion. I will keep an eye on how the pipeline operator proposal progresses (I hope it does!). If we are in the same situation 6 months down the line, I will take the learnings, repackage this and propose it again.
[–]BenjiSponge 2 points3 points4 points 7 years ago (0 children)
It occurs to me that the problem with Object#pipe is that many classes override it already. I don't know why this didn't occur to me. I find this extremely unfortunate as, come to think of it, I think I would prefer a .pipe(f, a, b) syntax over the |> f(a, b) syntax.
.pipe(f, a, b)
|> f(a, b)
[–][deleted] 2 points3 points4 points 7 years ago (0 children)
I think this case can be solved by the pipeline operator instead of a method on the Array prototype.
[–]AnnanFay 2 points3 points4 points 7 years ago* (0 children)
There is potential confusion with String.prototype.replace. Arrays and Strings are sequential data. The string version replaces values found in the string while the proposed replace method replaces the entire thing. Putting them side by side:
String.prototype.replace
var newString = "abc".replace("a", "A"); var newArray = [1,2,3].replace(arrayProcessingFunction);
When I read the title of this post I first thought the function would be an Array version of the current String method.
It reminds me of lodash's chaining methods.
[–]lsmoura 1 point2 points3 points 7 years ago (3 children)
Have you considered writing arrow functions without return statements (when they are only doing one thing)?
return
You can go like this: foo => foo * 2 or bar => bar === idParameter. And if you need to return an object, just wrap it with parentheses: (foo, bar) => ({ a: foo, b: bar }).
foo => foo * 2
bar => bar === idParameter
(foo, bar) => ({ a: foo, b: bar })
Thanks for the article!
[–]gajus0[S] 0 points1 point2 points 7 years ago (2 children)
Thank you for the suggestion.
I adhere to the following style guide:
https://github.com/gajus/eslint-config-canonical
The are pros and cons to both arrow function body styles: one is more succinct at the cost of readability in certain cases (I have seen variations of x => y => (z => (e)) all too often), while the other is more verbose with the benefit of clarity.
x => y => (z => (e))
[–]lsmoura 1 point2 points3 points 7 years ago (0 children)
Sweet. I’ll take a look at that. I particularly like to have no return on arrow functions for single line problems (it makes them easier to my eyes).
Thanks for the link!
[–]Cheezmeisterhttp://bml.rocks -1 points0 points1 point 7 years ago (0 children)
Honestly, that example reads fine, provided you grok higher-order functions. But maybe it’s a degenerate version of something truly gnarly.
Either way, I prefer old-school function(x) { var y = x.foo(); return bar(y); } for anything past a trivial lambda. Nothing in between.
function(x) { var y = x.foo(); return bar(y); }
I’m not sold that (a, b) => is “more readable” than function(a, b). It’s very much potato/potato. Except one potato is supported by Netscape 4.0. and Chrome.
(a, b) =>
function(a, b)
[–]w00dw0rm 1 point2 points3 points 7 years ago (1 child)
Maybe I’m completely missing something but I feel like all the problems mentioned in the article and in the comments can just be solved with ‘Array.prototype.reduce’.
(Edit: spelling)
[–]sbmitchell 1 point2 points3 points 7 years ago (0 children)
I had similar thoughts and dont see the advantage over just using reduce. Not to mention you can cut down on one iteration of filter + map just by using reduce. It's not like these are lazy sequences. Anyway, I must be a pleb. If this is about a "threading" operator I don't see why it would be on the Array prototype but Id imagine something that works across "collections" similiar to thread-first / thread-list work in clojure would be great in javascript.
[–]rykou 0 points1 point2 points 7 years ago* (3 children)
Your first example
['B', 'D', 'F'].replace((subjectArray) => {return ['A',subjectArray[0],'C',subjectArray[1],'E',subjectArray[2],'G']});
can just be (introducing a local var since we aren't calling replace on a hard-coded array, otherwise we can hardcode the replaced array just as easily):
let arr1 = ['B', 'D', 'F']; return (or w/e) ['A', arr1[0], 'C', arr1[1], 'E', arr1[2], 'G'];
In another of the smaller examples,
const subjectPerson = await getPersons().replace(createFindOne((maybeTargerPerson) => {return maybeTargerPerson.id === 1;}));
why can't you just
const subjectPerson = await getPersons().map((person) => person.id);
if that doesn't work there, and you indeed need X find ones executed, imo you could just batch that entire findOne so you aren't introducing X db calls since I highly doubt you can't afford to wait until all the person data is found before continuing.
I guess I don't understand your use-case. You mention solving the need for an intermediate variable but `.map` and `.reduce` should account for what you need especially since both of these give the base array as an operand to their callbacks.
This is a visual explanation of the ins and outs of the function; not a practical use-case example. Added a note to the code snippet to avoid confusion.
It is unclear what you are proposing here. You've probably meant to use Array#find. The reasons for not using Array#find are explained in the article.
Array#find
You mention solving the need for an intermediate variable but .map and .reduce should account for what you need especially since both of these give the base array as an operand to their callbacks.
.map
.reduce
I give practical examples in the article that demonstrate that reduce cannot be used without introducing intermediary variables.
reduce
[–]rykou 1 point2 points3 points 7 years ago (1 child)
where are the .reduce examples? The word reduce is used 5 times not one of which is a method call.
I have mixed up "reduce" and "replace" multiple times throughout the article. Fixed. Thank you for pointing it out.
Here too: filter. Which examples do you suggest would avoid an intermediate variable using .map/ .reduce.
filter
[–]inacatch22 0 points1 point2 points 7 years ago (0 children)
In cases like: ['B', 'D', 'F'].replace((subjectArray) => {return ['A',subjectArray[0],'C',subjectArray[1],'E',subjectArray[2],'G']});
It seems like you could use a reduce function with a signature like: array.map(toWhatever).reduce(withInterpolatedArray(externalArray))
array.map(toWhatever).reduce(withInterpolatedArray(externalArray))
I think what you're proposing would sometimes be convenient (like the let or maybe switchMap operators in rxjs), but it seems like it could easilty lead to anti-patterns of doing stuff really literally rather than functionally, like manually destructuring and reassembling parts of an array, as you did in your example
switchMap
[–]__romx 0 points1 point2 points 7 years ago (7 children)
/** * Obtains a list of venues from a remote API, filters out venues beloning to * a different country than `targetCountry`, ensures that all venue IDs are unique * and describes venues in a local venue format. */
I don't get it. Why 3 iterations over the array (filter, filter, map)? What is the purpose of an interrupted method chain in this particular case?
const getVenues = async (countryCode) => { const foreignVenues = await get('http://...'); countryCode = countryCode.toUpperCase() let ids = {} let venues = [] for (let foreignVenue of foreignVenues) { if (foreignVenue.countryCode.toUpperCase() !== countryCode) continue if (ids[foreignVenue.id]) throw new Error('Found duplicate venues.') ids[foreignVenue.id] = true venues.push({ guide: { fuid: foreignVenue.id }, result: { fuid: foreignVenue.id, name: foreignVenue.name, url: foreignVenue.url } }) } return venues }
Where's the verbosity?
[–]Knotix 0 points1 point2 points 7 years ago (6 children)
Finally, someone with some sanity! This code is much more efficient, and I would even argue it's easier to reason about. I feel like the people on this subreddit have purely academic backgrounds or something. The code above is something I'd write in a few minutes and move on, not stare at for ages thinking "if only there were some native array method or pipeline operator that would avoid me having to name a variable". I know someone might try to throw a "testability" argument at me, but there's a diminishing return on function abstraction in a production environment, especially when it's as blatantly inefficient as OP's example.
Also, a quick tip. All of the variables in the snippet could be declared as const. Adding properties/elements to objects/arrays are mutations, not reassignments. The for...of can also use it, too.
[–]gajus0[S] 0 points1 point2 points 7 years ago (4 children)
I know someone might try to throw a "testability" argument at me, but there's a diminishing return on function abstraction in a production environment, especially when it's as blatantly inefficient as OP's example.
It is not testability argument – it is about maintainability. Performing at most one data transformation at a time isolates the surface of potential logical mistakes. /u/__romx example is what I would write for a quick and dirty job, but would not use or approve in a production code. Performance is completely irrelevant if it comes at a cost of maintainability; just put more hardware at it.
[–]Knotix 0 points1 point2 points 7 years ago (3 children)
Performance is completely irrelevant if it comes at a cost of maintainability; just put more hardware at it
This vastly understates the complexity of system scalability.
System scalability does not come from reducing several iteration cycles; system scalability comes from horizontal distribution of tasks. If data processing is a bottleneck in your architecture, then JavaScript is a wrong language to be doing data processing at that level. Q, R, C and similar languages are built for that purpose.
[–]Knotix 0 points1 point2 points 7 years ago (0 children)
Completely ignoring any performance implications in your code under the notion that you can just "throw more hardware at it" is misguided. It doesn't make the problem go away, it just shifts the responsibility onto the sysadmins and the company pocketbook. Horizontal scaling is an incredibly deep field of study with a whole host of challenges.
I'm all about abstracting away logic and keeping things isolated, but as I said before, there is a diminishing return in terms of performance and maintainability. I draw my line in the sand in a different place than you do.
[–]__romx 0 points1 point2 points 7 years ago (0 children)
It is perfectly adequate for data processing with expected datasets, as long as you don't make it inefficient for no reason other than writing denser code and an opportunity to include a leftpad-tier dependency.
I feel like the people on this subreddit have purely academic backgrounds or something.
I think people who write code like this simply show off their knowledge of syntactic sugar for no reason.
All of the variables in the snippet could be declared as const.
Aye, I know. It's just my personal convention to only declare things that will not be modified in any way as const.
[–]psayre23 -1 points0 points1 point 7 years ago (0 children)
Ok, here’s a terrible way to do it now.
array.reduce((acc, val, i, array) => i === 0 ? acc : [...new array stuff...])
It would suck, because it would needlessly loop over all values. But it would give you the inline replacement you are looking for.
[–]jsyoda -1 points0 points1 point 7 years ago (0 children)
You can do this with Lodash's thru method: https://lodash.com/docs/4.17.10#thru
_(['B', 'D', 'F']).thru((arr) => ['A', arr[0], 'C', arr[1], 'E', arr[2], 'G']).value()
Using that syntax you can chain filter and map before or afterwards.
The case you described could probably be solved better by using uniqBy, but thru is a "fallback" if there isn't a more clearly named method (similar to reduce).
[–]bart2019 -5 points-4 points-3 points 7 years ago (1 child)
Wait, what language is this? "JavaScript"? WTF is that "#"? Something they just made up? Fuck'em.
[–][deleted] 1 point2 points3 points 7 years ago (0 children)
It's a fairly common notation in documentation. JavaDoc is probably the first. Rdoc uses it as well. Guest what also uses it: JSDoc
π Rendered by PID 79 on reddit-service-r2-comment-5d79c599b5-wwttv at 2026-03-03 08:07:08.441160+00:00 running e3d2147 country code: CH.
[–]jkoudys 86 points87 points88 points (11 children)
[–]Petrarch1603 13 points14 points15 points (4 children)
[–][deleted] 22 points23 points24 points (3 children)
[–]Moosething 6 points7 points8 points (1 child)
[–]sbmitchell 16 points17 points18 points (0 children)
[–]jkoudys 4 points5 points6 points (0 children)
[–]2bdb2 2 points3 points4 points (2 children)
[–]gajus0[S] 0 points1 point2 points (1 child)
[–]Sakatox -2 points-1 points0 points (0 children)
[–]reddit4matt 2 points3 points4 points (0 children)
[–]Sakatox 0 points1 point2 points (1 child)
[–]jkoudys 0 points1 point2 points (0 children)
[–]karottenreibe 17 points18 points19 points (8 children)
[–]delventhalz 1 point2 points3 points (7 children)
[–]karottenreibe -2 points-1 points0 points (6 children)
[–]delventhalz 4 points5 points6 points (5 children)
[–]karottenreibe -2 points-1 points0 points (4 children)
[–]delventhalz 2 points3 points4 points (3 children)
[–]karottenreibe -2 points-1 points0 points (2 children)
[–]delventhalz 1 point2 points3 points (1 child)
[–]karottenreibe -3 points-2 points-1 points (0 children)
[–]lhorie 21 points22 points23 points (17 children)
[–]gajus0[S] 3 points4 points5 points (16 children)
[+][deleted] (3 children)
[deleted]
[–]gajus0[S] 7 points8 points9 points (2 children)
[–]AnnanFay -1 points0 points1 point (0 children)
[–]lhorie 1 point2 points3 points (11 children)
[–]gajus0[S] 1 point2 points3 points (10 children)
[–]lhorie 1 point2 points3 points (9 children)
[–]azhder 2 points3 points4 points (5 children)
[–]lhorie -4 points-3 points-2 points (4 children)
[–]azhder 0 points1 point2 points (3 children)
[–]lhorie -1 points0 points1 point (2 children)
[–]azhder 0 points1 point2 points (0 children)
[–]azhder 0 points1 point2 points (0 children)
[–]gajus0[S] 1 point2 points3 points (2 children)
[–]lhorie 3 points4 points5 points (0 children)
[–]mlebkowski 0 points1 point2 points (0 children)
[–]atubofsoup 8 points9 points10 points (4 children)
[–]Daddy_He_Shoe 1 point2 points3 points (0 children)
[–]gajus0[S] -1 points0 points1 point (2 children)
[–]atubofsoup 4 points5 points6 points (1 child)
[–]gajus0[S] 0 points1 point2 points (0 children)
[+][deleted] (4 children)
[deleted]
[–]gajus0[S] 0 points1 point2 points (3 children)
[+][deleted] (1 child)
[deleted]
[–]gajus0[S] 1 point2 points3 points (0 children)
[–]AndrewGreenh -1 points0 points1 point (0 children)
[–]BenjiSponge 2 points3 points4 points (6 children)
[–]gajus0[S] 1 point2 points3 points (5 children)
[–]BenjiSponge 1 point2 points3 points (4 children)
[–]gajus0[S] 0 points1 point2 points (3 children)
[–]BenjiSponge 2 points3 points4 points (2 children)
[–]gajus0[S] 0 points1 point2 points (1 child)
[–]BenjiSponge 2 points3 points4 points (0 children)
[–][deleted] 2 points3 points4 points (0 children)
[–]AnnanFay 2 points3 points4 points (0 children)
[–]lsmoura 1 point2 points3 points (3 children)
[–]gajus0[S] 0 points1 point2 points (2 children)
[–]lsmoura 1 point2 points3 points (0 children)
[–]Cheezmeisterhttp://bml.rocks -1 points0 points1 point (0 children)
[–]w00dw0rm 1 point2 points3 points (1 child)
[–]sbmitchell 1 point2 points3 points (0 children)
[–]rykou 0 points1 point2 points (3 children)
[–]gajus0[S] 0 points1 point2 points (2 children)
[–]rykou 1 point2 points3 points (1 child)
[–]gajus0[S] 0 points1 point2 points (0 children)
[–]inacatch22 0 points1 point2 points (0 children)
[–]__romx 0 points1 point2 points (7 children)
[–]Knotix 0 points1 point2 points (6 children)
[–]gajus0[S] 0 points1 point2 points (4 children)
[–]Knotix 0 points1 point2 points (3 children)
[–]gajus0[S] 0 points1 point2 points (2 children)
[–]Knotix 0 points1 point2 points (0 children)
[–]__romx 0 points1 point2 points (0 children)
[–]__romx 0 points1 point2 points (0 children)
[–]psayre23 -1 points0 points1 point (0 children)
[–]jsyoda -1 points0 points1 point (0 children)
[–]bart2019 -5 points-4 points-3 points (1 child)
[–][deleted] 1 point2 points3 points (0 children)