how to group an array based on a specific function? - javascript

Here, I am new to JavaScript. I am solving questions; however, I am having a problem understanding chaining more than one method together. I have been trying to understand this solution, but it took me a lot of time, and I still don't get it.
I understand that I will input the array that I needed to change according to the specific function, which I opted. I understand all of methods functions, but I don't understand their syntax here, so can someone please explain each step to me ?
const group_By = (arr, fn) =>
arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val, i) => {
acc[val] = (acc[val] || []).concat(arr[i]);
return acc;
}, {});

In as few words as possible.
Firstly they compute a ternary expression, here they are checking if the input is a function, if it is they pass it as is, otherwise they create an anonymous function that tries to access the given property. The arrow function after the colon can seem a little confusing but it's still just a function. It takes one argument called val, and returns property which key is inside the fn variable.
typeof fn === 'function' ? fn : val => val[fn]
The next step is to create a new array with new values for each of the elements. Output of this step is just a list of values to group elements on.
For instance calling it on array ["a", "bb"] with a fn='length' would return [1,2]
arr.map(typeof fn === 'function' ? fn : val => val[fn])
Then they call the .reduce function on the output array. The purpose of the reduce function is to create a single value out of all the elements slowly iterating over it. You can tell that the last step returns accumulator value back, and that it is passed as a first argument to the function called on the next element. The empty object at the end is just an initial value of the accumulator.
.reduce((acc, val, i) => {
...
return acc;
}, {});
And finally for the step that does the accumulation. Here firstly the val from the result of the map, is used to access property of the newly created object. If the value does not exist it replaced with an empty array || []. That has the element of the initial array at the same index concatenated onto it. If there were some elements it just adds new ones to it and reassigns the value.
acc[val] = (acc[val] || []).concat(arr[i]);

Okay, what I understood from your query is that you are trying to chain multiple functions together.
function Chained() {
// this reference.
const _this_ = this
this.Function1 = () => // do something and return _this_
this.Function2 = () => // do something here and return _this_
}
Above you can see that chain is a simple object which returns "this" as context. and on context, we already have Function1 and Function2. so due to this following will be valid.
const _ExecuteChained = new Chained()
// Now you can run _ExecuteChained.Function1().Function2() and so on.
Now coming to your code.
const group_By = (arr, fn) =>
arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val,
i) => {
acc[val] = (acc[val] || []).concat(arr[i]);
return acc;
}, {});
Here you are just running a loop on arr and validating if the second param is a function or not if function then return it as is (Since you are using a map it will be stored at the given index. else just get the value of the function at the given key and return it.
Reduce.
in Reduce you are trying to accumulate a given value with (contact or merge or extend) value at a given index of arr in this case function.

Related

Callback Function is Undefined Within Itself

I have a callback function:
function map(item, callback) =>
Object.fromEntries(
Object.entries(item).map(callback)
)
const replaceDollarCb = ([key, value]) => [key.replace('%$%', '$'), typeof value === 'object' && value !== null && !Array.isArray(value) ? replaceDollarCb(value) : value]; // Here I get "undefined is not a function".
UtilObject.map(subQuery, replaceDollarCb);
It keeps failing to find the function itself (replaceDollarCb). I've used this and even changed them to non-arrow functions.
It's not that the method is undefined within itself, it's that you're not passing the expected array of key & value as arguments.
You'll need to recall your method with map not directly replaceDollarCb to make it work as I think you expected.
const map = (item, callback) =>
Object.fromEntries(
Object.entries(item).map(callback)
)
const replaceDollarCb = ([key, value]) => [key.replace('%$%', '$'), typeof value === 'object' && value !== null && !Array.isArray(value) ?
map(value, replaceDollarCb) :
value
];
const subQuery = {
"foo%$%": {
"subfoo%$%": "bar"
}
}
console.log(map(subQuery, replaceDollarCb));
Hi :) I'm not sure I understand your question, but map is reserved word in JavaScript as map function - thank you, Quentin, on arrays - exists and defining/overriding it (I'm not sure what's first lines meant to do) is bad idea.
Next problem is that you use replaceDollarCb before defining it. Did you mean recursion?
EDIT:
I'm also not sure why you "define" map first and use UtilObject.map too. Maybe that's something that could help you :)

JavaScript function return False and 0 respectively

I am new to JavaScript and I am trying to write functional Programming to calculate if a number is Odd (isOdd) or not. I do not understand why I've got true and 0 or false and 1 when I called isOdd() twice in a row.
here is my code:
var mod = m => {return number => {return number % m }}
var eq = number1 => {return number2 => {return number1 === number2}}
var combine = (...fns) => { return arg => {for(let fn of fns.reverse()) arg = fn(arg); return arg;}}
var eq1 = eq(1)
var mod2 = mod(2)
var isOdd = combine(eq1, mod2)
mod2(4) -----> returns 0
mod2(3) -----> returns 1
eq1(1) -----> returns true
eq1(2) -----> returns false
isOdd(1) returns true
isOdd(1) returns 1 (what??)
I can not understand what I missed or what goes wrong. I tested this code in most browsers.
Appreciate if someone can explain in details.
I can wrap isOdd again and have something like
isOdd = (number) => {return Boolean(combine(eq1, mod2))}
that returns boolean value everytime. But I want to understand what I missed in the first place/
Array.prototype.reverse reverses the fns array in-place. You’re calling .reverse on fns every time you call isOdd, even though you pass a fixed argument list of functions in combine once. That list is scoped to combine and reversed on every call of isOdd(1).
In other words, your first isOdd(1) call remembers the fns array of your original combine(eq1, mod2) call. fns is [ eq1, mod2 ]. Then you call fns.reverse() to iterate over it; but this mutates fns to [ mod2, eq1 ]. You get the correct result, because you wanted to call the functions in reverse order. The resulting call is eq1(mod2(1)) === eq1(1) === true.
In your second isOdd(1) call, however, fns is still remembered, and it still has the reversed [ mod2, eq1 ] value, because its scope is combine, so another isOdd call doesn’t reset the originally passed fns. The second isOdd call reverses this array again because fns.reverse is called within isOdd. The resulting call is mod2(eq1(1)) === mod2(true) === 1 (because true % 2 is coerced to 1 % 2).
A working function would look like this:
const combine = (...fns) => {
const reversedFns = fns.reverse();
return (arg) => {
for(let fn of reversedFns){
arg = fn(arg);
}
return arg;
};
};
A simpler approach uses Array.prototype.reduceRight:
const combine = (...fns) => (arg) => fns.reduceRight((result, fn) => fn(result), arg);
Since you want to aggregate multiple operations onto a single result based on a list from right to left, reduceRight is the perfect method for this. You start with arg, go through the list of functions from right to left, then call the current function with arg, and that becomes the new arg for the next function. The final result is returned. All this is captured by the code fns.reduceRight((result, fn) => fn(result), arg).

findKey() - recreating lodash library method

I've got a problem with a CodeCademy task. I am to re-create the findKey lodash library method. Here there are the steps of how to do it, but I got stuck, especially at point 5.
Add a method to our _ object called findKey.
Add two parameters to this method: object and predicate. We will
name our predicate function parameter predicate since this is the
name used in the Lodash documentation.
Within the method, use a for ... in loop to iterate through each key
in object.
Within the loop, create a variable called value and set it equal to
the value at the current key in object.
Still within the loop, create another variable called
predicateReturnValue and set it equal to the result of calling
predicate with value.
Finally, still within the loop, use an if statement to check
if predicateReturnValue is truthy. If it is, return the current key
from the method.
Outside of the loop, return undefined to address all cases where no
truthy values were returned from predicate.
This is my code that doesn't work:
findKey(object, predicate) {
for (let key in object) {
let value = object[key];
let predicateReturnValue = predicate(value);
if (predicateReturnValue === 'true') {
return value;
};
};
return undefined;
}
I appreciate your help!
You need to return the key after the truty check of the call of predicate.
function findKey(object, predicate) {
for (let key in object) {
let value = object[key];
let predicateReturnValue = predicate(value);
if (predicateReturnValue) { // just take the value
return key; // return key
}
}
}
const
isStrictEqual = a => b => a === b,
object = { a: 'foo', b: 'bar', c: 'baz' }
console.log(findKey(object, isStrictEqual('bar')));
console.log(findKey(object, isStrictEqual('cat')));

How do I assign my .reduce function output to a variable?

Given an object and a key, getElementsThatEqual10AtProperty returns an array containing all the elements of the array located at the given key that are equal to ten.
I want to assign what my .reduce method outputs to a variable then use that variable inside my if statement. My code evaluates to NaN.
function getElementsThatEqual10AtProperty(obj, key) {
var arrayTotal = 0;
obj[key].reduce(function(a,b){
arrayTotal = a + b;
});
if(arrayTotal === 10) {
return obj[key];
}
};
You're approaching reduce incorrectly. You're almost using it like Array#forEach. Remember that callbacks are functions. They expect to return an actual value.
When you read the docs, you'll see that reduce also needs an accumulator, that is an argument after the callback.
const sum = obj[key].reduce(function(a,b){
return a + b
}, 0)
Or if you want the newer and prettier version:
const sum = obj[key].reduce((a,b) => a + b, 0)
When you have a one liner, return is implicit, which means it's implied, as opposed to explicit.
The accumulator is what your value starts at while it totals up the values.
Array.reduce(callback, accumulator)

Reduce function extra bracket meaning - JS

I have a function:
function ross(array) {
return array.map(function(data) {
return data.reduce(function(obj, item) {
obj[item[0]] = item[1];
return obj;
}, {});
});
}
ross(array);
Basically this code converts the 3 dimensional array to an array of object. I want to focus on this part:
return data.reduce(function(obj, item) {
obj[item[0]] = item[1];
return obj;
}, {});
Under the return obj, you'll see another extra {} brackets beside the comma (,). Not sure what it really does if it's a callback or what. But I tried to played with it by changing it to [], and it's actually changing the output into two dimensional array.
Anyone out there who could explain what the extra bracket does?
Those {} define an empty object. The second argument to reduce is a seed value for the accumulator that's passed to your callback. So that {} is defining what your callback sees as obj on the first call (and all subsequent calls, because it returns obj).
That code is functionally identical to:
var obj = {};
data.forEach(function(item) {
obj[item[0]] = item[1];
});
return obj;
reduce is just being used (some would say abused, since the accumulator never changes; but it's really common) to make that one outermost statement rather than three.
From MDN: "The reduce() method applies a function against an accumulator and each element in the array (from left to right) to reduce it to a single value."
The second parameter is the initial value of the accumulator, which comes into your function as the parameter obj. If you do not specify an accumulator the default is the first element from the array that reduce is invoked on.

Categories