Solving a JavaScript Array Reduce Interview Question

preview_player
Показать описание
Рекомендации по теме
Комментарии
Автор

There is new sugar in JavaScript, you can use Logical OR assignment as tallyArray[name] ||= { } or the same thing with Nullish coalescing assignment (??=)

marcusaureo
Автор

Learned something today: (the nullish coalescing assignment)
```
function addItUp(...arrays) {
return arrays.flat().reduce((tally, { name, ...points }) => {
console.log(`Working on ${name}`);
console.log(points);

tally[name] ??= {};

Object.entries(points).forEach(([key, val]) => {
tally[name][key] ??= 0;
tally[name][key] += val;
});

return tally;
}, {});
}
```

drw
Автор

Nicely done. Learned a lot from this, thanks!

LuisReyes-zsuk
Автор

The shortest I could make it. Sometimes it's fun to write the shortest function you can make.

The general idea is that "points" is actually an object and that means that we can change its values and later read what we had changed. Now we just need to check the value in the talllyMap and add it to points if it exist. Also used map instead of forEach because it's shorter lol

const addItUp2 = (...arraysOfData) => {
return arraysOfData.flat().reduce((tallyMap, { name, ...points }) => {
Object.keys(points).map(key => points[key] += tallyMap[name]?.[key] ?? 0);
return {...tallyMap, [name]: points};
}, {})
};

Disclaimer: Don't actually do this outside of silly code challenges

noctemcat_
Автор

Understanding reduce's initialValue parameter and the fact that reduce can return multiple types of your choosing was key to my understanding of its power and utility. Lots of array methods return arrays or boolean values or length values, but reduce can return anything you want.

bradbt
Автор

2 and half years ago I would be completely lost, now I find this very easy.

IkraamDev
Автор

I love these JS challenges. Thank you. May I have another?

bmehder
Автор

To get the unique index of name you can create a set from the arrays. Then from your set of unique index you can reduce over the array to get your value. Another way to remove name from an object is to set name to undefined this will remove it from the object without having to create a new variable using the spread destructing.

rtorcato
Автор

if tallyArray[name] doesn't exist, just copy everything from item (clone); there is no need to check every properties of the item and copy to a new object

QuangLe-hdoc
Автор

I've learned something new I'd like to share, Optional chaining with bracket notations (it's just like optional chaining with dots but I didn't know it existed)

function addItUp(...arraysOfData) {
const data = arraysOfData.flat();
const tally = data.reduce((added, { name, ...player }) => {
for (const [key, value] of Object.entries(player)) {
added = {
...added,
[name]: {

(added?.[name]?.[key] || 0) + value,
},
};
}
return added;
}, {});
return tally;
}

carlosjimenez
Автор

Can you talk about how passing async to a map or filter function doesn't act how you would think?

omomer
Автор

What are you pressing on vscode to show all those types?

ThomazMartinez
Автор

this works preety well too
function responseArr(...rest) {
const data = rest.flat();
const returnData = data.reduce((prev, curr) => {
const{name, ...rest} = curr
prev[curr.name] = {...rest}
return prev
}, {});
return returnData;
}

harshpatel
Автор

10:20 could you have coded tallyArray[name][key] += val; // instead?

patcoston
Автор

ArraysOfData auto takes two const arrays ?

evgenyzhithere
Автор

I shortened up the `tallyArray[name][key]` using the same `||` pattern you used earlier. Other than that it's exactly the same way I would have written it!

function addItUp(...arraysOfData) {
const data = arraysOfData.flat();
const tally = data.reduce(function(tallyArray, item) {
// first check if this person is new
const { name, ...points } = item;
tallyArray[name] = tallyArray[name] || {};
// Loop over each of their properties and add them up
Object.entries(points).forEach(([key, val]) => {
tallyArray[name][key] = tallyArray[name][key] || 0;
tallyArray[name][key] += val;
})
return tallyArray;
}, {});
return tally;
}

You could get really fun with the reduce and forEach returns and wrap them in nested destructuring but that would make it much less readable IMO.

markhuot
Автор

How I'd re-write it:

const arraysOfObjectsReducer = (...arraysOfObjs) => {
return arraysOfObjs.flat().reduce((acc, obj) => {
const { id, ...rest } = obj
acc[id] = acc[id] || {}
Object.entries(rest).forEach(([key, value]) => {
acc[id][key] = (acc[id][key] || 0) + value
})
return acc
}, {})
}

I don't think this is any less readable. I've just used some more generic terms and simplified the consolidating of the values. I've also just returned the chained function calls on the object instead of declaring two variables and then returning the last one.

taylorsabbag
Автор

Wouldn't recommend using an object as an initial value of a reducer function, since with every iteration the a new object is created. It's better to use a map for this and convert it to an object later

plusquare
Автор

Well. Well well well.

Tried that myself and it gave me a throbbing headache - seeing your much more elegant and overall just better approach clearly doesn't quite help that much either.

At some point I just didn't care about nesting or code readability as a whole for that matter any more, just wanted to get it over and done with. Also, scrolling through the comments section REALLY makes me wonder where I swerved off the road like that...

However, it works as intended. IT WORKS AS INTENDED! ...that's the most important part, right? RIGHT?

Also expanded the functionality a bit, so everything with matching keys where the value is not of type number gets shoved into an array (except for the "name" property).

All right, here we go, brace for impact (apologies for any nausea symptoms caused):

const result = addItUp(arr1, arr2);
function addItUp(...arrs) {
const combinedArr = arrs.flat();
const mutatedArr = combinedArr.reduce((acc, cur) => {
const curInAcc = acc.find(obj => obj.name === cur.name);
if (!curInAcc) {
acc.push(cur);
} else { // cur in acc
const indexOfcurInAcc = acc.indexOf(curInAcc);
const keysInAcc = Object.keys(curInAcc);
const keysCur = Object.keys(cur);
for (const keyInAcc of keysInAcc) { // // if curInAcc[key] in cur[key], mutate it
const matchingKey = keysCur.find(keyCur => keyInAcc === keyCur);
if (matchingKey) {
(typeof curInAcc[matchingKey] === "number") { // type number
= + cur[matchingKey];
else { // not type number
{ // notArray
(matchingKey !== "name") { // notName
= [curInAcc[matchingKey], cur[matchingKey]];

else { // Array



}
}
for (const keyCur of keysCur) { // if cur[key] not in curInAcc, insert it
const matchingKey = keysInAcc.find(key => key === keyCur);
if (!matchingKey) {
= cur[keyCur];
}
}
}
return acc;
}, []);
return mutatedArr;
}
console.log(result);

christian-schubert
Автор

My values are not adding to the aggregation, this just bringing a string ;/

phillfreitas