Reducing complexity with cleaner code results in faster development time for all. The idea of better code readability is highly underrated and the more advanced our developer is, the easier it is for anyone to read and understand the purpose of it.
@Benimation4 жыл бұрын
This is not the video title you want to see after having used it twice today..
@diegovideco4 жыл бұрын
If readability === communication of intent, then I'd rate as follows (filter >= map > reduce > for loops): 1. filter: may only shorten array 2. map: will only convert array elements from one type to another 3. reduce: will only return value of the type of the accumulator 4. for loops: may do any or all of the above plus then some (need to read function in full to understand intent) For loops may mutate one value, but they may mutate more than one, plus have side-effects, so one never knows. For reduce one only needs to look at the accumulator to see what's the result (if the actual type of the accumulator is not immediately obvious please document). Loops outside of functions are even worse than complex reduce outside of functions, because they often need to create variables outside their scope. It's true that reduce invites this precisely because it does not need to create unnecessary variables. But anyways it's an unfair criticism. In the end if people in the team don't want to learn reduce (not that hard, really), therefore not being to express or communicate intent with it, then that's a better reason not to use it than "readability" as an abstract universal thing.
@RyanTosh4 жыл бұрын
The only use case for reduce() I've found is getting the sum/average of an array. Also, couldn't reduce still return a different type? Say, [].reduce((acc, el) => "hi", 4);
@diegovideco4 жыл бұрын
@@RyanTosh well, in JS you can sure do all sorts of unintended stuff because of the lack of a type system. That's why I put an emphasis on _intent_ and communication. Try doing that in a strongly typed language and the compiler will complain. What I mean is, as rule to use and understand reduce, if you intend your function to return a string then put a string in the initial value of the accumulator, and so on with any other type. In JS this responsibility is laid on the programmer, but the discipline it requires is good and helpful.
@diegovideco4 жыл бұрын
As far as uses go, there's a lot. Basically anything that may need to accumulate a value. They are kind of the functional and more declarative substitute to loops (because the accumulator states the type, and de discipline), and an alternative to recursion. Many higher-order functions can be implemented with reduce, including map and filter. But also very useful things that JS still lacks, like groupBy, partition, or pipe. Get to know and give yourself the opportunity to use it and you'll find many more, and you'll see if you enjoy it or not. It's really not bad in itself. Also read about fp in JS, there's a lot of good free stuff around. Once again, no one is saying you can't do all that with loops, but the whole reduce is bad meme/dogma makes it look much harder than it is (it's as hard as learning loops in the first place, just from a different point of view... some people don't like change or go beyond what they are used to, or going back to square one, but it does not mean it more complex). Kind of shuts off the possibilities of the language (and prevents people from more easily venturing into other functional languages that can be very interesting and joyful, like clojure/script, elm, haskell, etc).
@welltypedwitch4 жыл бұрын
@@diegovideco I 100% agree with you :)
@benjaminbenson28174 жыл бұрын
Perhaps I'd add the array function "sum". like some >= filter >= map > reduce > for loops
@ghostinplainsight48034 жыл бұрын
I use reduce anytime I need to make an array of things into a single thing, that's it's purpose and it communicates it better than a loop communicates it's purpose because a loop is too abstract and I need to read the code inside and often outside to understand what it is doing. I just read a single function for reduce and I can understand what it is doing. The code you showed was overcomplicated.
@halfalligator65183 жыл бұрын
totally depends what you're reducing and how you're doing it. The point is that reduce is often not helping at all.
@Kaldrax4 жыл бұрын
My main usage for .reduce() is the combination of .filter() and .map() because it only iterates once over the array and in contrast to using a loop you don’t have to define the output variable first.
@malydok2 жыл бұрын
Did you try .flatMap()? Might fit your use case.
@antifa_communist Жыл бұрын
What do you mean output variable?
@dibaliba4 жыл бұрын
Yes, I did read. I am a fan of reduce, but I only use it to do simple things. Extracting the callback become a function with name is also a game-changer, make code more expressive.
@ilovecomputers4 жыл бұрын
>"Don't use reduce, it's hard to understand and show off-y" >suggests using iterators
@cat-.-4 жыл бұрын
Lol he's not telliing you to use iterators. He's got iterators all confined in his well-named functions!
@mishasawangwan66523 жыл бұрын
uh yeah. 100%.
@justafreak15able3 жыл бұрын
atleast 60% of developers can understand what filter and maps are.
@clickrush4 жыл бұрын
I agree with the sentiment that reduce can typically be mis-/overused since it is so close to a generic loop. It is also very easy to accidentally re-implement more specific higher-order functions such as filter/map/flatMap/find/every and so on, because reduce is so generic. But one important aspect which was largely left out in this discussion is that both reduce and(!) a reducer are functions and thus closures. They can be composed, be part of a composition, be passed from another place to capture a frame, they can be called within other contexts and so on. Yes you could wrap a loop into a function to achieve similar things but then you would just re-implement reduce. Yes, when thinking of using reduce and writing a reducer one should very likely look for an alternative first, usually composing more specific functions is the way to go. But by writing specific for loops one almost always loses the opportunity for a declarative approach and possible abstractions. On a more general note I disagree with the idea that code should always look as simple as possible (as proposed in the video). In the first draft or exploratory phase I would recommend it, but sufficiently abstracted functional code can often easily lead to separating concretions (AKA data), which enables code-reuse and testability. So when given the opportunity and seeing a possible gain one should consider this. Data Driven code with generic functions is often much more declarative and easy to understand than 'simple' code if carefully written.
@Isti1154 жыл бұрын
Finally, someone with a higher level view, not just looking at the direct readability gain, but also the potential further advantages, which in my opinion far outweigh the negative aspects, and also, I think that there's a valid reasoning behind getting people smarter, so they won't have trouble reading more abstract code.
@Slashx922 жыл бұрын
"Yes you could wrap a loop into a function to achieve similar things but then you would just re-implement reduce". Or implement a function that only creates one result variable and mutates it. Even with that statement reduce can easily be overruled by a simpler and better ad-hoc function.
@andreasherd91310 ай бұрын
what you describe are basically transducers. they implement map/filter and other things as a reducing function. composable, re-usable and skips intermediate allocations. It's always sad to see people arguing that stuff they don't understand or they find hard to read is bad. We are in this to learn things not diss them.
@MichaelBckerLarsen4 жыл бұрын
Episode should've been called: "Turn it into a loop"
@beyondant4 жыл бұрын
19:51 I guess the simplier way is to just use edges.filter(v => v && v.isSeen).length;
@ChazAllenUK3 жыл бұрын
Unless you work at Google... then it's a loop!!
@Buslowicz4 жыл бұрын
Surma: you can make it a map, wrap it in a function, or write a loop Jake: WRITE A LOOP!!!!!!!!
@frameshifty4 жыл бұрын
This should be renamed to "Is bad code bad?" The examples used as arguments against reduce are obtusely clever and clearly should be broken down into easier to read code.
@sney20024 жыл бұрын
flattening an array before .flat() existed function flat(array) { return [].concat.apply([], array); }
@Jespertheend24 жыл бұрын
Jhonatan Sneider Salguero Villa their example also shows the spread operator. Didn’t flat exist before the spread operator?
@sney20024 жыл бұрын
@@Jespertheend2 according to caniuse flat was added after the spread operator spread operator = chrome 49, firefox 34 .flat() = chrome 69, firefox 62
@ColinRichardson4 жыл бұрын
would Array.prototype.concat be more appropriate for that? So you don't make an additional array??
@sney20024 жыл бұрын
@@ColinRichardson Yep, that would be better.
@SargsyanTigran4 жыл бұрын
"Reduce is bad." Surma: "wrap it in a function and hide it out of my sight!" 🙈
@isfland4 жыл бұрын
We can write smart code. Pure code. Most performant code. But I like that your north star is readable code and you prefer the most readable option among all examples.
@rlamacraft4 жыл бұрын
I like the format of finding code in the real world and suggesting improvements (Paul’s original supercharged was always very entertaining) However, I think the conclusion that reduce is bad is completely off. I could come up with “clever” unreadable loops just as easily as I can with reduce. Really the conclusion should be: *abstract units of computation into small functions* and let the tooling optimise the code shipped to users. This isn’t always the case (chaining map and filter is obviously better than reduce), but even then I would abstract them out to their own function.
@dassurma4 жыл бұрын
Are you saying that the title is clickbait??! No wait, I said that at the start of the video ^^
@leonk69504 жыл бұрын
One use for reduce is when actually working with *reducers*. When you’re doing a lot of list-processing, using reducers is a great way to help readability. (A reducer is any function that could be given to reduce, I.e. sum (+), max, min, etc. creating custom reducers for your data processing pipelines can be really helpful, especially when working with dynamic stuff (say the user can choose if he wants to see min, max or total of a list of numbers. Instead of calling different functions, you can just have one variable „selectedReducer“ and put that into array.reduce, reducing complexity a lot and also making your code more expressive, as you’ve now put the users selection into a variable which directly matches his intent and is directly used as such
@MaxArt25014 жыл бұрын
I mostly use .reduce for object building, partitioning and grouping (other than summing things together). As JavaScript evolved, the legitimate use cases for .reduce has been... reduced. But I still need to use it if my target environment would need a polyfill that I'm not allowed to provide.
@YuFanLou4 жыл бұрын
MaxArt Reduce is the most generic recursion form there is. It has the same power as for loop. It is good practice that functions closer to the use cases are built on top of it and replace it in usage, just like how you write a function to contain a for loop. I just wish they stop treating it like it’s inferior just because they are not as familiar with it. Reduce is the same age as for loop. It was invented as fold in Lisp in the 1950s, along with for loop in ALGOL.
@elCoronelCC4 жыл бұрын
23:12 I assume the code can be simplified: data.notifications.edges .filter(v => v && v.isSeen) .length Code in the video: data.notifications.edges .map(v => v && v.isSeen) .filter(Boolean) .length
@abubakr3803 жыл бұрын
Actually .filter(Boolean) removes undefined and null values in the array
@SantoLucasST3 жыл бұрын
@@abubakr380 the first filter is already filtering the null and undefined, right?
@abubakr3803 жыл бұрын
@@SantoLucasST Yes it does remove null and undefined values. However the filter function will return an array of "edges", while the map function will return an array of booleans. Therefore, the two approaches serve completely different purposes.
@kyzanhyuri53564 жыл бұрын
8:29 This was the point that made me decide to reduce my use of reduce. I didn't really think about it but it is bizarre to have the parameter at the end of the logic. Which you then would have to run through the sequence of logic again with the new knowledge of what's being fed first. It just breaks flow state. It is far smoother to have the parameters defined first and then read the loop logic.
@diegovideco4 жыл бұрын
here's why it can be a good idea to have data come last kzbin.info/www/bejne/o2TWp36Fmb-coKM&app=desktop Also it is a more or less standard order for reduce functions across many different languages
@mo3ath124 жыл бұрын
var arr =[1,5,6,4,5,95,5,5,]; Let sum = arr.reduce((s=0,t)=>s+t); You can utilize default value for the first argument , now it makes a lot of sense
@MarioAndreschak4 жыл бұрын
The fact that you answered the question straight away and instead of beating around the bush for 14 minutes made me like and subscribe.
@ChazAllenUK3 жыл бұрын
Is reduce() bad? - maybe Is making *everything* a loop bad? - yes *lodash* provides clean solutions to lots of reduce-like problems: keyBy, mapValues, invert, mergeWith, ...
@BudgieMind Жыл бұрын
It's amazing how, just in time, this video popped up in front of my eyes. I was writing a reduce function that was returning a map and literally used a snippet g = new Map from the video
@magne60493 жыл бұрын
18:33 For a slight win in readability, don't use `.filter(Boolean)`: // If you have an array with potential null values: const array = [1, 2, null, 3] // Don't do: array.filter(Boolean) // since: "wtf, it's an array of integers, not booleans?!" // Do: const isNonEmpty = f => f // you can define it once and for all somewhere else array.filter(isNonEmpty) Make it read out loud and clear. Don't make the reader have to think. Also, with respects to "That's a fun trick": Tricks are a code smell.
@rishabh_gour4 жыл бұрын
I don't always use reduce(), but when I do, I do that to look smart :)
@LasanaMurray2 жыл бұрын
Use reduce() to reduce a list of values into a single value, it's that simple.
@wdnspoon Жыл бұрын
On TypeScript, reducing into a Map has an advantage over the loop in that you can build a mutable Map, and return a ReadonlyMap.
@LuLeBe2 жыл бұрын
Oh wow I used it at times to build strings. Basically the sum-like thing, maybe to put together all the option tags in a select based on an array and also have a default option (which is defined in the initial value). Could also be done with loops. I think reduce is not terribly hard to read for these cases though, where you basically just add the current array value (maybe with some transformation first) to the sum of the previous values. Certainly easier to understand for non-JS devs than stuff like spread operators etc.
@welltypedwitch4 жыл бұрын
I personally use reduce in the way that you would use foldl in Haskell (foldl is the same as reduce). I basically use it as an easier way to do tail recursion, when explicit recursion is too verbose (So the function should be rather short or some function you have already implemented). Example: foldl (+) 0 [1..5] Or in Js: reduce ((x, y) => x + y, [1, 2, 3, 4, 5]) With explicit recursion this would be: let f [] = 0 f (x:xs) = x + f xs in f [1..5] Or in JS: const f = (ar) => { if(ar.length === 0) return [] const [x, ...xs] = ar return x + f(xs) } f([1,2,3,4.5]) Btw. The reason the callback comes first is, because in an implicitly curried language like haskell you can just omit the array in a point free style :) So my main Point is that you find loops easier to read than reduce, because you're not used to that kind of recursion, but someone coming from a language like Haskell (like me) may actually find the reduce function much cleaner and easier to read.
@TheAxeForgetsTheTreeRemembers4 жыл бұрын
Yes, but the problem is what you are using the language for. In their case it's for web apps, where people are not used to math or functional programming. The most important thing is to keep consistent with the context you are writing code in. (I believe) Interesting explanation for the order of the arguments btw.
@welltypedwitch4 жыл бұрын
@@TheAxeForgetsTheTreeRemembers Sure, but (at least in my opinion), programming for web apps is such a big field, that you should not limit it to one style of programming (especially if that style is known to be quite error prone). Also JS/TS is not only used on the frontend, but also in node (which is where I use it). Still, I totally agree with you about keeping the code simple and consistent (especially if it's open source) :)
@tomtrask_YT4 жыл бұрын
Around 19:30. That conversion to Boolean in a filter does not filter out empty objects. An empty object is still truthy. It would filter out zeroes (numbers) and empty strings but supposedly those would break that node.isSeen test because strings and Numbers do not have a node property. A notification that lacked the node property would also break that example whether it was empty or not. The filter saved nothing. The rewritten code suffers from this same defect.
@andreo45114 жыл бұрын
If you're just using reduce to keep the function pure there are other ways to do this. The most important characteristic of a pure function is that it's 'referentially transparent'. That means that you should be able to replace a function with its return value and have everything still behave the same. So you could replace reduce with forEach and just wrap it in a function so it doesn't mutate anything outside the function However if you may still need reduce if you want your code to be very concise or you need to chain functions
@myFBisXapu4 жыл бұрын
Almost every day part of my job is to aggregate data from various places and then show it somewhere over a web page (I assume this is the case for most FE developers) and so far `reduce` is one of my favorite tools for the task. In my opinion, when you understand how `reduce` works and if used on a daily basis, the snippets of code including `reduce` become quite readable and reasonable. Another thing that i like when using `reduce` (and chaining methods over iterable stuff) is the avoidance of mutations, which can be quite a painful problem when using series of loops to aggregate a set of data. Of course the extrems are bad and instead of ``` [].filter(Boolean) .reduce((count, { node })) => (node.isSeen ? count : count + 1, 0) ``` I will use some build-in methods like ``` [].map(v => v && v.isSeen).every(Boolean) ``` I'm fully aware that if you separate your code in small functions, that use loops and imperative style internally the returned result will be the same (in some cases it might be even more optimized) as if using more functional approach, (assuming that the inner works of the function work fine and there are no leaky parts :D ), but still, I think that reduce has its advantages in a lot of cases and sometimes saves a lot of writing time.
@konstantinkh4 жыл бұрын
Reduce exists because it's a good paradigm in parallel computation. I know how to take a block of code using reduce that's taking too long and split it into tasks that won't without having to figure out what it was that you were trying to do. If you keep writing custom code for each of these, then you might as well just start from scratch any time you have to optimize anything. I understand that multithreading is a dirty concept to a lot of JS devs, but if you want time-to-interactive not to start skyrocketing now that we've hit the limit of what a single thread can do, you're basically going to have to learn to deal with it. And you might as well be getting used to map/reduce paradigms now.
@jakearchibald4 жыл бұрын
Reduce doesn't work in parallel in JavaScript. But how would it work in parallel anywhere? Each function call depends on the result of the last
@konstantinkh4 жыл бұрын
@@jakearchibald You generally need additional constraints. The simplest constraint is if b.reduce(F, a.reduce(F, z)) === a.concat(b).reduce(F, z) for given choice of F, z and suitable split of desired array into partitions a and b. Accumulation of a sum from zero is a trivial example. A more sophisticated example would be something like standard deviation computation, where the above constraint doesn't hold, but can be modified by keeping track of partition sizes to be true. Situations where the result truly depends on the specific sequence of inputs are rare in practice, and almost entirely limited to cryptography and hashing. A reduce problem can frequently be partitioned in a sensible way. Unlike map, you don't just let the machine handle this, of course. The algorithm has to be designed with a particular partitioning strategy in mind. But that's precisely why use of map and reduce as keywords are useful even compared to forEach. They signal the intent. I know that I can just split maps across cores without even thinking about it. I know that I'll have to take greater care with reduce, but I also know which patterns to look for in these to satisfy possible constraints. If I see a loop that does a bit of both, I know absolutely nothing without digesting the entire algorithm and probably re-implementing it from scratch to allow for parallelism. And yes, there are definitely misuses of reduce out there. And so many calls to reduce should really be calls to .map(...).reduce. But that's hardly a problem with the reduce itself.
@patfix4 жыл бұрын
Thanks Lucas!
@dwighthouse4 жыл бұрын
Basically the only time I use Reduce is when I want to "filter" an object. fromEntries() is not supported everywhere yet and it requires creating a ton of tiny, intermediate arrays. But I agree with the sentiment of writing immediately clear code. That's why I recommend never using classical inheritance or any other mechanism that involves the possibility of spooky execution at a distance (proxies, getters, setters). Instead, use normal data structs and pure functions (or mostly pure functions) to operate on that data. If structural control/security is required, it can be added at the item level or the api level with closures or (now) module boundaries.
@jakearchibald4 жыл бұрын
Using it to reduce objects is one of the worst cases of reduce. You're either passing the same object around in a loop (meaning you're only using the looping mechanism of reduce), or creating a new object each time which is really slow. Loops are faster and IMO are easier to read.
@dwighthouse4 жыл бұрын
@@jakearchibald You're probably right. I'm still getting off the escalator of everyone telling me "Use map! Use filter!", when loops were perfectly fine in the first place. Change takes time. Still, if there were a nice way to filter Objects, this would have been avoided in the first place.
@mrhappy1924 жыл бұрын
A good workaround for reduce's argument order is to just use one: array.reduce( (sum = 0, current) => current + sum )
@csorfab4 жыл бұрын
This doesn't do what you think it does. The default value provided for sum will never get used. From MDN: "If no initialValue is supplied, the first element in the array will be used and skipped" developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce Try this: [1,1,1].reduce((sum = 5, current) => current + sum) The result, according to you, should be 8, but it is actually 3.
@lordfo994 жыл бұрын
Just a hate talk, totally omitting any good code examples using reduce. I like some of your code rewrites, and agree with them, but the general message of this talk is bad. You just say, code simple, don't care about performance and scoping, just mutate, do side effects and create potential bugs.
@benjaminfrost15834 жыл бұрын
They didn't say they didn't care about performance, just that it was secondary to readability in most instances. The cost/benefit tradeoff there is super situation specific.
@MishaAmashukeli4 жыл бұрын
Reduce is never faster than a for/foreach, so I don't know why you mention performance. The benefit of reduce over for loops is that it makes(should make) code simpler to understand, it can not give you a guarantee that the code inside is bug-free. When it actually makes the code harder to understand, then this code will be more error-prone and there is no reason to use reduce in such cases.
@marcusbrsp2 жыл бұрын
This video could have been reduced to three seconds, 1:29 - 1:32.
@JDalmasca2 жыл бұрын
Credit to Jake for pointing out that the syntax for reduce() is terrible. The initial value goes at the end? No one thinks that way. I'm glad someone else sees that as problematic.
@api-first4 жыл бұрын
Is the function at 8:41 broken? In the comment for this function it stated that the function should return [1, 3, 6, 7] for an input of [1, 2, 3, 1] - but it returns [1, 2, 4, 7, 8]. It always just takes the last number in arr, adds to it the next input value, and concats it to the arr. In order to work as described in the comments if would need to sum all items prior to the next input value.
@csibesz074 жыл бұрын
You are right, there is a bug and i'm really surprised they didn't catch it with their 20years of experience :), one solution: return input.slice(1).reduce(...) , the issue is reduce start at index 0, but we already counted it with [input[0]].
@davidtheprogrammer4 жыл бұрын
That code that's supposed to return a running total is incorrect. I ran it and the results don't match the expected result in the comment above. Also I was able to do it with a loop... Check below ``` function loopImplementation(input) { let currentTotal = input[0]; const finalArray = []; input.forEach((value, index) => { if(index == 0) { finalArray.push(currentTotal); } else { currentTotal += value; finalArray.push(currentTotal); } }); return finalArray; } ```
@mrusagi10044 жыл бұрын
Yes, that confused me as well. Just to add something, a correct reduce implementation could look something like the following. But I guess at that point a loop implementation would be preferable. relativeToAbsolute(input) { return input.reduce( (arr, val) => { return arr.concat(arr.length > 0 ? arr[arr.length - 1] + val : val); }, [] ); }
@emilemil14 жыл бұрын
Oh dear god that relativeToAbsolute. 1. It's incorrect. With the input given in the comment it produces [1,2,4,7,8]. Really goes to show how easy it is to botch things when using Array.reduce. 2. It doesn't have any advantage when it comes to speed compared to simpler solutions. 3. It's not quick to write or easy to read. This function using map has equal performance (on Chrome), is faster to write, and is easier to understand (+ it actually gives the correct result): function relativeToAbsolute(input) { let accumulated = 0; return input.map(val => accumulated += val); }
@Idolaughtmyheadoff4 жыл бұрын
I use reduce mostly in cases where a need to create multiple arrays from single array by some criteria. For example in array of entities I need two arrays: removed and alive entities. It can be replaced with calling filter two times over this array, but reduce will be better because it iterates over array once.
@dassurma4 жыл бұрын
As I say in the video, I think these are often unnecessary performance optimisations. Most of the time, arrays are not big enough to have a meaningful performance difference. I'd rather have expressive code. And even after all that, creating multiple arrays from one is a case for a loop, not a reduce() function.
@drissAithafid4 жыл бұрын
TLDR: In most of the examples you used I agree with you if you're talking about small arrays. You're right that readability comes first over performance, but actually, if we're working with a huge array we should consider performance first. like for example, in the last example about images.thumbnail you're code is more readable but in case of huge arrays your code rewrite is less performant because you're iterating over the array of images twice (map() + filter() ), while the original one iterates just once using reduce().
@fateriddle144 жыл бұрын
I actually find while reduce doing reduce things(meaning turning an array into a string), they are quite readable, like arr.reduce((a,b) => a+b)
@tobyfried4 жыл бұрын
Agreed. The functional-style array methods really shine when used for quick one-liners.
@danilaplee4 жыл бұрын
reduce looks more readable for me than your loops, maps & function chains, which like you said require generators to be optimal in performance and async generators are any programmers worst nigthmares (and don't work in ts) :)
@jasonleelawlight4 жыл бұрын
Personally I don' have problems with reduce() as long as the logic in the callback is very simple and easy to follow, and most of the examples in the video are not like this. In general if I see someone making up unnecessarily complicated stuff I would ask them why they are doing so and how they can simplify and refactor it, this will discourage those who just want to show off without any concerns of code quality.
@microcontrolledbot4 жыл бұрын
So, your issue with reduce is the argument implementation due to the callback not being the last argument. Which I agree with, I've always hated specifying the accumulator as the final argument but reduce is amazing and I will not use it any less in favor of loops. It has it's place and I for one welcome it with a open heart and mind.
@cameronm2564 жыл бұрын
For a couple of these I noticed that haskell, a language designed for functional programming, has a function more suitable than reduce built in which would make the implementations a lot clearer: * For the `relativeToAbsolute` example, `scanl`: hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#v:scanl * For the "jsx join" example, `intersperse`: hackage.haskell.org/package/base-4.12.0.0/docs/Data-List.html#v:intersperse Is the issue here just that JS lacks the tools for doing FP?
@jasonleo4 жыл бұрын
Editor Lucas is really cool!
@blipojones21144 жыл бұрын
Aside from actual code. The best use of reduce is to ask about it in code interviews, usually when we review a fake PR together. It's not a bad way of finding out how far along the "learning journey" a dev actually is, regardless of years experience. Are they just on it cause its trending? they even recognise it may be harder for others to read? So ye, it's handy for "reducing" a pool of candidates.
@lilkwarrior55614 жыл бұрын
Many would find `forEach` far more "readable" than `for of`. Also, there's other values than readability why some might find `reduce` far better than `for of`-particularly some would want to avoid for whatever reason the added global variables that a for-of loop requires. Accordingly it's extreme I think to say devs are "abusing" reduce for things like the graph reduce example. The other elephant in the room is that there's a significant amount of JS developers that prefer the composable functional approach of applying transformations to looped code that reduce helps facilitate (transducers) that enhances readability subjectively for those people. Overall, i think Google should do less videos like this that's on subjective matters such as readability versus more quantitative things like performance.
@jakearchibald4 жыл бұрын
for-of does not require global variables
@vladislavstepanov75914 жыл бұрын
In my opinion, readability is more important than perfomance cuz we usually work with small amount of data
@lilkwarrior55614 жыл бұрын
Vladislav Stepanov I’m not weighing the importance of Readability vs performance. I’m saying it’s not objective and is a bad fit for this show that primarily is usually just Jake & Shurma who do amazing jobs on a variety of other topics. It’s an incredibly subjective topic that at least needed more representation of devs involved. Readability is very contextual that spans far more than the opinion of 2 programmers here who happen to be on the same page. You need to account how familiar a team is with certain topics, acknowledge your own bias as a programmer who has decided to use certain languages that come with built-in biases towards what you can do & not (impacting the rate you’re exposed to alt ways of doing the same things you’ve done); & so on… It’s quite telling the examples of this particular episode didn’t involve devs who have styles or have code bases they feel strongly about the use of reduce over loop; it’s common knowledge there’s meaningful amount of devs who do considering among the top libs in all of JS are functional & include what many find better versions of reduce for styles of programming a significant portion of JS devs find far more readable than loops (reactive functional programming comes immediately to mind)
@tjkandala56504 жыл бұрын
Thanks for bringing up transducers. You really can get the best of both worlds (readability/composition and performance) with transducers. It becomes easier to understand and change each step of the process with transducers, which the main value proposition of using methods like reduce(). I wrote a pseudo-transducer (github.com/tjkandala/obsducer/blob/master/README.md#performance) which performed almost as well as for loops on huge arrays. My "transducer" is a hack built with RxJS (because I wanted to use RxJS operators more often lol. Glad you brought up reactive programming as well), but many people have written "proper" transducers a la Clojure with similar performance profiles. My transducer in practice (github.com/tjkandala/xplosive/blob/master/src/workoutGenerator/calculateIntensity.ts#L70). I didn't go through a CS degree so to me, for loops genuinely take more work. I prefer to offload as much "mental state" as possible to my computer (with TypeScript, simple abstractions) so I can commit my working memory to solving problems. reduce() helps me write code that would probably be really buggy if I wrote it the "easy way," by way of hiding implementation details.
@TheNewton4 жыл бұрын
"Google should do less videos like this that's on subjective matters such as readability versus more quantitative things like performance" ROFLLOLOLOLOL what? The depth of irony that statement is for. This like how Crockford got grief for encouraging readable code and linters to minimize bugs instead of doing stuff that makes compiler jocks happy, or people were just used to getting away with. Google's been one of the largest proponents on web performance, pushing a lot of the cultural standards in modern web development. So when their devs talk about readability being more important than performance that's a clue that readability influences and *scales* performance of software development on the web. A mistake made in this video and other such talks is treating readability as 100% subjective, when the problem is that we lack the tools and concepts to measure readability objectively as all modern trends have been towards speed metrics mostly AFTER the fact also because it's such a large problem that goes beyond soft-dev a problem mostly solved by having the ethos of readable code before worrying about performance. If you need an objective litmus test just put all your code through a minimizer, delete all your old unminimized code and from then on only codegolf in minified javascript and measure your error rates.
@magne60493 жыл бұрын
15:32 A for loop would still be much easier to read (and require less knowledge) than using Object.fromEntries, Object.entries, and map. See how clear it is, even when constructing an object: ``` const events = Object.keys(this.state.events) const handlers = {} for (const event in events) { handlers[event] = this.eventDidFire.bind(this, event) } ```
@DrRobrez4 жыл бұрын
My most common usage of reduce is to create a dictionary of some sort from a list of objects.... I always struggle to write these vs a loop that does the same thing
@Luxalpa4 жыл бұрын
use Object.fromEntries() and map()
@eboubaker37224 жыл бұрын
Yes i read the video description
@BlackKillerGamer4 жыл бұрын
usually the problem is not the reduce; it's the function that is passed to it, as is with all other higher-order functions. just give it a name: list.reduce(sum) // instead of list.reduce((a, b) => a+b) list.filter(exists).reduce(join(', ')) // instead of list.filter(x => !!x).reduce((acc, x, i) => i !== 0 ? [...acc, ', ', x] : [x], []) // or whatever btw join would be implemented like const join = sep => (a, b, i) => the above... (though I'd prefer pairing everything with a comma and stripping the last one after, so you don't have to think like you're in a loop with an iterator / in a zip with a range) imo that way the code is rather close to natural language, as long as the function names are meaningful
@olimsaidov4 жыл бұрын
The term "sequential code" is misused. The only reason why reduce doesn't look like "sequential code" it's because the callback function usually inlined. But you are free to separate it as a named function for readability. Small functions with small localized scope are better then large chunk of "sequential code" with common scope.
@jakearchibald4 жыл бұрын
Putting the callback elsewhere makes the code less sequential. If you're going to make a callback anyway, put the whole operation in there and use a loop.
@I-M-Pulsive4 жыл бұрын
Why not use CSS for the map function where it wants to join the links with a comma?
@jackbennett27604 жыл бұрын
I think this is right, your rewrite of flat only flattens 1 level of array? The reduce will flatten any number of nested arrays.
@tjerkannemeesters62384 жыл бұрын
The relativeToAbsolute() function doesn't actually do what the code comment says; it returns [1, 2, 4, 7, 8]
@nialltracey25994 жыл бұрын
I don't like the syntax of .reduce (or .map or .filter) but the concept is sound. I personally love Python's list comprehensions and generator expressions, because they offer a clear, concise depiction of the intended function (and before anyone says "custom for loops are more efficient", maybe so, but haven't you ever heard the expression "optimise late"?). .reduce's problem is that by being just another function with a general reliance on unwieldy unnamed functions, the syntax isn't clear, so isn't as expressive as the underlying idea is. I can't think of a way to make reduce visually expressive in plain text (or I'd have submitted an RFC to the Python maintainers ages ago!) but it does encapsulate a single logical idea into one thing. The alternative is trying to decode a much longer piece of code to work out a) what it's supposed to do and b) what it actually does.
@aokihu4 жыл бұрын
REDUCE() is a powerful function, it can solve many problem with simple logic
@jakearchibald4 жыл бұрын
You could say the same about loops
@aokihu4 жыл бұрын
@@jakearchibald REDUCE is like loop, but it is very useful in functional programming
@jakearchibald4 жыл бұрын
It's good for sums, but pretty difficult to read for everything else, compared to loops. For what it's worth, the creator of Python agrees www.artima.com/weblogs/viewpost.jsp?thread=98196
@lilkwarrior55614 жыл бұрын
Jake Archibald The Python creator bit can easily be seen as an appeal to authority fallacy.
@MaximillianHeth4 жыл бұрын
Is it that difficult to understand how reduce works though? In functional programming, reduce is extremely useful for functional composition in ways that would be far more convoluted to replicate with either map or filter.
@stannone72723 жыл бұрын
I hope Surma, did not get sick too.
@JonAthan-ln2wn4 жыл бұрын
There is actually quite some reduction happening in Surmas underdash. 😬😄
@lilkwarrior55614 жыл бұрын
Readability is such a subjective thing, especially when it comes to JS where a lot of styles are supported and what langs & edu you had throughout your programming years. It doesn't really matter how long you program (2 years, 50 years, whetever), if you haven't been exposed not at all to a different style of programming primarily devoid of what you've used over your experience, of course some things are considered far less readable to you. Let's say if someone's first language was Haskell and they primarily only used functional programming, they'll find some of these examples frustrating and less readable than the imperative code written. The values you pick up subjectively throughout your career you didn't have when you first program you learn greatly impacts your lens on readability. For example, IDK about the graph example being more readable than the reduce version; to me the reduce version was more readable and it was ANNOYING you had to create a global variable to generate the graph. That's nitpicking of course, but my point is that this video was often doing appeal to authority fallacies on why the reduce one was worse than the `for of` which is less readable to many simply because it's `for of` instead of another syntax some find more "readable" such as "forEach".
@jakearchibald4 жыл бұрын
You keep saying global variables, but this isn't a requirement. You can create variables in block & module scope.
@lilkwarrior55614 жыл бұрын
Jake Archibald That’s what I meant; sorry that was confusing about my related messages about this. That said, that’s undesired by some as unnecessary cognitive noise.
@KManAbout2 жыл бұрын
i mean sure if we lived in a world where most people's first language was haskell. it largely is not though. haskell is not very popular nor are any functional languages
@lilkwarrior55612 жыл бұрын
@@KManAbout I mean there has been many times throughout history various philosophies and things were prevalent at the expense of other philosophies, things, and even people that better served problems/society.
@Isti1154 жыл бұрын
8:30 No, no, no, no, please! Think about generalization, currying! This argument order matches that of Haskell's fold and others. (Everything should be as close to functional programming as possible is my basic approach, because I've seen how helpful that can be over the last several years.)
@r-gart4 жыл бұрын
You know, that's just your opinion, man.
@Isti1154 жыл бұрын
@@r-gart Yes, I know, that's why I included that it's "my approach", but I would really like to let others know the importance and value of these concepts and design decisions before they just give up on them saying they're too complicated.
@mattmccarthymusic4 жыл бұрын
Editor having fun at the end!! Haha
@ColinRichardson4 жыл бұрын
I got my current job because I knew how to use arr.reduce(). So can't be that bad. But that last part, is that not just sequence library?
@ColinRichardson4 жыл бұрын
He, not anymore. We started from scratch in a different language. But yes I agree with you. They must have been using it as a easy barrier of entry. There was about 25 questions on test. I answered the first 3, 2 of which were reduce, and they stopped there and then. I didn't leave the car park before I was told I had the job (which was nice, but very shocking).
@KelvinWKiger4 жыл бұрын
@@ColinRichardson Shocking indeed, reduce is really not that hard compare to a classic sorting algorithm optimization question. Lucky you, keep it up!
@ColinRichardson4 жыл бұрын
Sorting algorithm questions would not help that much here.. We don't do sorting on our data. Ordered by edited order. More recent it was edited, the higher in the list it is.. Full Stop. :D Maybe I got the job on my sexy looks instead. Though, I have 2 games companies for past jobs on my CV. I found that to almost be like a "Get into any job you want easily" pass.
@KelvinWKiger4 жыл бұрын
@@ColinRichardson Aaaaah... The sexy look, now your story makes sense.
@ColinRichardson4 жыл бұрын
And my modesty.. I am the most modest person to have ever existed. :D
@Zorgatone4 жыл бұрын
At 15:18 there’s an error in the events refactored with fromEntries and entries (namely .bind(this, value) .bind(this, key))
@naythaniel4 жыл бұрын
The max number of function arguments seems to be just memory limited. So it would probably end up with the same result for any given array of values in the Math.max example. No?
@31redorange084 жыл бұрын
I think I've hit the limit before.
@mithunsatheesh4 жыл бұрын
A talk emphasising code readability where none of the code samples shown are actually readable by human eye. Very unique !!.
@truvc4 жыл бұрын
For the data.notifications.edge, where they’re trying to get the number of unseen notifications the .map() seems unnecessarily complicated. You could filter out the falsy and unseen notifications in .filter().
@KevinFarrugia4 жыл бұрын
I think that readability is subjective and dependent on what you are used to. People used to say that JSX is hard to read but now have gotten accustomed to it.
@impero1014 жыл бұрын
I completely agree. People coming from a OOP or imperative background, will rate readability differently than people coming from a functional, declarative background.
@jessepoulton2244 жыл бұрын
Don't even need the .map, no? `edges.filter(v => v && v.IsSeen).length`
@roceb50094 жыл бұрын
I feel like people wouldn't use reduce as a replacement for for loops if reduce wasn't just a wrapper for a for loop and an accumulator. like, if it actually did a proper MPI/MapRedeuce-style reduce instead of this iterative accumulator-based implementation
@franklemanschik_de4 жыл бұрын
@14:20 your pointing out functions do cost nothing but thats not correct it depends on the call order and pushes and pops the call stack as also creating context objects :) they have cost and i boost performance and read ablitity via reducing function invocations and replace them with apply on the same context,
@kettlebellsporttraining15074 жыл бұрын
I would say that the reduce method should be considered , per se, a language feature that aims to convey a peculiar meaning like: I’m going to iterate over something and transform it into something else. So, when we encounter a reduce statement, we know right from the start what it stands for, and this fact should be taken into account while talking about code readability. Moreover I think that the readability problems arising from the reduce function are more tied to the “indiscriminate” use of anonymous arrow functions or impure functions or simply bad or not reader friendly Javascript. Simply defining a named function or declaring a named arrow function increases in a significant way the readability of the code (not to mention that it makes things easier to test when needed). So I would not put the reduce method to sleep, not just yet..
@dassurma4 жыл бұрын
We didn’t say to put it to sleep. As we said multiple times, put it in a named function to make it expressive.
@Nielsblog4 жыл бұрын
20:26 This is probably my most common usage for .reduce(). I don't see how the proposed alternative is better. I expect that other people can read what .reduce does, since this does require you to read it inside-out like some of the other examples. And most importantly, unlike the alternative you only do the iteration once, making it a simple O(n) operation instead of O(n + n). (Sorry, had to look smart at the end there, it comes with using .reduce() all the time).
@dassurma4 жыл бұрын
Well, as I said in the video, it is _sometimes_ used as a performance optimization, bu I highly doubt your array is long enough to sacrifice expressiveness for the couple of nanoseconds that you gain. (Also O(n+n) is the same as O(n), just sayin’)
@HawkingsCt4 жыл бұрын
If you're concerned about optimization you should use a loop instead of reduce
@Nielsblog4 жыл бұрын
@@dassurma My main argument is that is this instance I find reduce _more_ expressive than the proposed alternative.
@dassurma4 жыл бұрын
@@Nielsblog I hear ya. I think that _might_ apply to functional programming languages. And maybe if you choose to write functional-only on JavaScript, that might be the right way to go for your project. In general I don’t consider JavaScript a functional programming language and find reduce() less idiomatic than a simple loop. That’s why I wanted to look at code examples from real code bases. I found those definitely less readable than the imperative equivalent.
4 жыл бұрын
Interesting episode but I wish there were some examples of good use case for reduce function.
@jakearchibald4 жыл бұрын
It's ok for accumulating totals
@soulehshaikh87994 жыл бұрын
Yes, I always read video descriptions especially if it is from a leading tech firm channel like Google.
@a9jc143 жыл бұрын
Is the presentation or list that they use in this episode available?
@mokkamokka40974 жыл бұрын
I love you guys!
@danylogudz4 жыл бұрын
19:51 should count “not seen”, but implementation counts “seen” But the way is understandable
@j3bb9z4 жыл бұрын
Also, there is missing "node" key (destructured in original implementation). And also, there is no need to map before filtering. data.notifications.edges .filter(edge => edge?.node?.isSeen === false) .length; But yeah, still easier to read without the reduce. I think.
@dassurma4 жыл бұрын
Ah yeah, both comments here right. But you get the gist :D
@Tolgeros4 жыл бұрын
Try iteratively building a Promise chain (NOT a Promise.all) without reduce. With reduce, it's trivial: const pChain = MAPPING_ARRAY.reduce((p, mapping) => { return p.then(() => { /* create and return a promise using mapping */}) }, Promise.resolve())
@jakearchibald4 жыл бұрын
I prefer async/await for this developers.google.com/web/fundamentals/primers/async-functions#example_outputting_fetches_in_order
@leonnilein4 жыл бұрын
What do you think about .filter(f => !!f) instead of .filter(Boolean)? More expressive, less expressive, antipattern?
@dassurma4 жыл бұрын
None of these patterns are great, tbh
@jakearchibald4 жыл бұрын
There's also f => f == true, but yeah, few good options
@MaxArt25014 жыл бұрын
I think the most expressive code would be .filter(s => s !== 0) or something like that. Double bangs are a bit jarring, as well as .filter(Boolean) (that I'm giulty of using from time to time).
@GreenZ3r04 жыл бұрын
What about? xyz.EXISTS_FILTER = Boolean; xyz.UNIQUE_FILTER = ...; ... array.filter(xyz.EXISTS_FILTER)
@jsmunroe6 ай бұрын
I've been coding for 20+ years as well, and I understand reduce really well, but I've never wanted to use it because it is so muddy.
@MrKarameyl4 жыл бұрын
Even if JavaScript is not a pure functional language, it does not mean that writing in functional style is less readable because in case of the first function, this would be far more readable in my opinion. repl.it/repls/RecentFormalPup Just because you read and master reduce does not mean you master functional programming. It is a really small water droplet in the ocean of features that functional programming offers.
@dassurma4 жыл бұрын
I didn't say less readable. I said less idiomatic.
@Isusia4 жыл бұрын
If someone worried about intermediate arrays there is another approach called transducers. But them are even more complicated than a reducers. Can you made another video about them, bring some light and your opinion? Thank you.
@kerbatonbaton81083 жыл бұрын
8:38 does not work like intended? if i input [1, 2, 3, 1] i get back [1, 2, 4, 7, 8] but it should be [1, 3, 6, 7] function relativeToAbsolute(input) { return input.reduce((arr, val) => { return arr.concat([arr[arr.length-1] + val]); }, [input[0]]); } relativeToAbsolute([1, 2, 3, 1])
@blenderpanzi2 жыл бұрын
20:06 But very strictly speaking this is **not** functional. Calling methods is object orientated. Functional would be having nested function calls.
@josephwong28324 жыл бұрын
That was a great debate. I've always had to spend extra time wrapping my head around reduce any time I use or see it.
@KarlOlofsson2 жыл бұрын
I used reduce to go through every element and select the largest recently. A more readable way would have been to have a variable for the end result, and a loop comparing and storing any new larger value, but it seemed so messy. Any suggestions for a more readable but equally concise/functional way to solve this? :)
@valour.se474 жыл бұрын
I always read the description of videos longer then 20 minutes to find the TL;DR version.
@sidvishnoi14804 жыл бұрын
There is also a proposal (Stage 2) for iterator helpers: github.com/tc39/proposal-iterator-helpers, which should counter the intermediate object argument.
@dassurma4 жыл бұрын
Oh NICE! I did no know about this proposal.
@stepankrivanec46444 жыл бұрын
Hello beautiful people at @Google Chrome Developers The code shown on 7:36 does not produce expected result (the one in code comment) The code given there will return [1, 2, 4, 7, 8] not [1, 3, 6, 7] for input of [1, 2, 3, 1] Have a nice day.
@the-old-channel4 жыл бұрын
What if I need to flat an object with some additional parsing, is it ok to use reduce? rows.reduce((a, b) => { const object = JSON.parse(b["data"]); return { ...a, ...object }; }, {});
@jakearchibald4 жыл бұрын
This performs pretty badly, like O(n^2) badly. A regular loop will be much faster, and easier to read.
@welltypedwitch4 жыл бұрын
@@jakearchibald I would disagree about the readability, but why would a loop be that much faster?
@jakearchibald4 жыл бұрын
@@welltypedwitch because a regular loop would only create one object, whereas this reduce example is creating n, and performs two inner iterations for every outer iteration
@joe_navy_s34 жыл бұрын
I use reduce() if the resulting array is a reduced version of the initial array
@Luxalpa4 жыл бұрын
Use filter()
@joe_navy_s34 жыл бұрын
@@Luxalpa yeah but i meant to say the resulting answer is a reduced version of the initial array, as in (filter+map)
@FireNeslo4 жыл бұрын
Main problem with reduce is the arguments being in the wrong order. To me that's the main unreadable part otherwise it would mostly read like a loop.. Reducing state based on events for example can be very nice.
@Luk4zs4 жыл бұрын
they are not in the wrong order
@TheHeadlets4 жыл бұрын
@@Luk4zs Of course they are. They even explained it in the video - callbacks always go last as by their nature they are called back last. It's the same with `setTimeout` and `setInterval` where the callback is first for seemingly no reason.
@Luk4zs4 жыл бұрын
@@TheHeadlets Well, the accumulator parameter is optional, therefor it's after callback parameter, so they are not in wrong order.
@LowPolyPixel4 жыл бұрын
I don't think I've ever used reduce(). But that's because I usually forget it exists and it feels really unintuative when I do remember its existence.