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.
I came across this code when I checked how to find the number that occurs odd number of times.
I tried to understand everything but I ca figure this out.
Please tell what is happening step by step and what each variable means.
Thank you!
function findOdd(A) {
let counts = A.reduce((p, n) => (p[n] = ++p[n] || 1, p), {});
return +Object.keys(counts).find(k => counts[k] % 2) || undefined;
reduce is an Array method which should reduce an array to a single value, which could be an array itself, an object or any flat variable type
reduce will take a function and an initial accumulating object as parameters
The function is called for each element in the array and be passed the accumulating object as first parameter (p) and the single array item as second parameter (n)
The function is in this case an arrow function expression
The function body consists of two expressions connected by the comma operator. The comma operator will return the rightmost expression as a result, in this case p.
The first expression of the comma operator (p[n] = ++p[n] || 1) will return 1 if p[n] is not set, otherwise will increment p[n] and return the result. Thanks to the short-circuit evaluation of the logical OR (||)
This could be written a little bit more detailed as
A.reduce(function (p, n) {
if (p[n]) {
p[n] = p[n] + 1
} else {
p[n] = 1
}
return p
}, {});
As a result you receive an object which counts the appearance of every value in the array
Then you have the return statement which will return the first key of counts which has an odd value
It does this by first creating an Array of the keys of counts with Object.keys.
It then iterates over those keys and checks, whether the modulo division by 2 (% 2) of the value corresponding to every key is a truthy value (in this case not zero) and then return that key.
It will always return the first key with that property
At the end this found value is converted to a number with the unary plus operator
If no value was found, undefined is returned
2 methods are used are used here that you need to understand.
reduce : read about it here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
find read about it here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
the below statement makes a map of a number vs its occurence
let counts = A.reduce((p, n) => (p[n] = ++p[n] || 1, p), {})
and then the second statement searches the map for an element which occurs odd number of times and if no such element it found it returns undefined
return +Object.keys(counts).find(k => counts[k] % 2) || undefined
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')));
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.
Do you have any real-world example of the use of the second and third parameters for the callback to Array.prototype.some or Array.prototype.any?
According to MDN:
callback is invoked with three arguments: the value of the element, the index of the
element, and the Array object being traversed.
I've personally never used them.
I have been working for some time on the Javascript functional programming library, Ramda, and early on we made the controversial decision not to use the index and array parameters for other similar functions that we created. There are good reasons for this, which I don't need to get into here, except to say that for some functions, such as map and filter, we find such extra parameters do have some occasional utility. So we offer a second function which supplies them to your callback. (For example, map.idx(yourFunc, list).)
But I've never even considered doing so for some or every. I never imagined a practical use of these. But there is now a suggestion that we include these functions in our list of index-supporting ones.
So my question again is whether you have ever found an actual, live, real-world callback function to some or every which actually needs these parameters? If so, could you describe it?
Answers of "No, I never do," would be helpful data too, thanks.
Quick search in our code:
function isAscending(array) {
return array.every(function (e, idx, arr) {
return (idx === 0) ? true : arr[idx-1] <= e;
});
}
I could imagine something like the following code to check whether an array is duplicate-free:
….every(function(v, i, arr) {
return arr.indexOf(v, i+1) == -1;
})
Where … is a complex expression so that you'd really have to use the arr parameter - which is no more an issue if you'd properly factor out the functionality in an own function that takes the array as an argument.
The second parameter can be useful sometimes, but I support your position that it is rather seldom used.
Yes, they are helpful
These extra parameters actually do come in handy, but not that often.
In the recent past, I had written a function to find all the permutations of a list of elements:
permute :: [a] -> [[a]]
For example permute [1,2,3] would be:
[ [1,2,3]
, [1,3,2]
, [2,1,3]
, [2,3,1]
, [3,1,2]
, [3,2,1]
]
The implementation of this function is quite simple:
If the input is [] then return [[]]. This is the edge case.
If the input is say [1,2,3]:
Add 1 to every permutation of [2,3].
Add 2 to every permutation of [1,3].
Add 3 to every permutation of [1,2].
Of course, the function is recursive. In JavaScript, I implemented it as follows:
var permute = (function () {
return permute;
function permute(list) {
if (list.length === 0) return [[]]; // edge case
else return list.reduce(permutate, []); // list of permutations
// is initially empty
}
function permutate(permutations, item, index, list) {
var before = list.slice(0, index); // all the items before "item"
var after = list.slice(index + 1); // all the items after "item"
var rest = before.concat(after); // all the items beside "item"
var perms = permute(rest); // permutations of rest
// add item to the beginning of each permutation
// the second argument of "map" is the "context"
// (i.e. the "this" parameter of the callback)
var newPerms = perms.map(concat, [item]);
return permutations.concat(newPerms); // update the list of permutations
}
function concat(list) {
return this.concat(list);
}
}());
As you can see, I have used both the index and the list parameters of the permutate function. So, yes there are cases where these extra parameters are indeed helpful.
However, they are also problematic
However these superfluous arguments can sometimes be problematic and difficult to debug. The most common example of this problematic behavior is when map and parseInt are used together: javascript - Array#map and parseInt
alert(["1","2","3"].map(parseInt));
As you can see it produces the unexpected output [1,NaN,NaN]. The reason this happens it because the map function calls parseInt with 3 arguments (item, index and array):
parseInt("1", 0, ["1","2","3"]) // 1
parseInt("2", 1, ["1","2","3"]) // NaN
parseInt("3", 2, ["1","2","3"]) // NaN
However, the parseInt function takes 2 arguments (string and radix):
First case, radix is 0 which is false. Hence default radix 10 is taken, resulting in 1.
Second case, radix is 1. There is no base 1 numeral system. Hence we get NaN.
Third case, radix is 2 which is valid. However there's no 3 in base 2. Hence we get NaN.
As you see, superfluous arguments can cause a lot of problems which are difficult to debug.
But, there is an alternative
So these extra arguments are helpful but they can cause a lot of problems. Fortunately, there is an easy solution to this problem.
In Haskell if you want to map over a list of values and the indices of each value then you use do it as follows:
map f (zip list [0..])
list :: [Foo]
[0..] :: [Int]
zip list [0..] :: [(Foo, Int)]
f :: (Foo, Int) -> Bar
map f (zip list [0..]) :: [Bar]
You could do the same thing in JavaScript as follows:
function Maybe() {}
var Nothing = new Maybe;
Just.prototype = new Maybe;
function Just(a) {
this.fromJust = a;
}
function iterator(f, xs) {
var index = 0, length = xs.length;
return function () {
if (index < length) {
var x = xs[index];
var a = f(x, index++, xs);
return new Just(a);
} else return Nothing;
};
}
We use a different map function:
function map(f, a) {
var b = [];
if (typeof a === "function") { // iterator
for (var x = a(); x !== Nothing; x = a()) {
var y = f(x.fromJust);
b.push(y);
}
} else { // array
for (var i = 0, l = a.length; i < l; i++) {
var y = f(a[i]);
b.push(y);
}
}
return x;
}
Finally:
function decorateIndices(array) {
return iterator(function (item, index, array) {
return [item, index];
}, array);
}
var xs = [1,2,3];
var ys = map(function (a) {
var item = a[0];
var index = a[1];
return item + index;
}, decorateIndices(xs));
alert(ys); // 1,3,5
Similarly you can create decorateArray and decorateIndicesArray functions:
function decorateArray(array) {
return iterator(function (item, index, array) {
return [item, array];
}, array);
}
function decorateIndicesArray(array) {
return iterator(function (item, index, array) {
return [item, index, array];
}, array);
}
Currently in Ramda you have two separate functions map and map.idx. The above solution allows you to replace map.idx with idx such that:
var idx = decorateIndices;
var arr = decorateArray;
var idxArr = decorateIndicesArray;
map.idx(f, list) === map(f, idx(list))
This will allow you to get rid of a whole bunch of .idx functions, and variants.
To curry or not to curry
There is still one small problem to solve. This looks ugly:
var ys = map(function (a) {
var item = a[0];
var index = a[1];
return item + index;
}, decorateIndices(xs));
It would be nicer to be able to write it like this instead:
var ys = map(function (item, index) {
return item + index;
}, decorateIndices(xs));
However we removed superfluous arguments because they caused problems. Why should we add them back in? Two reasons:
It looks cleaner.
Sometimes you have a function written by somebody else which expects these extra arguments.
In Haskell you can use the uncurry function to solve this problem:
map (uncurry f) (zip list [0..])
list :: [Foo]
[0..] :: [Int]
zip list [0..] :: [(Foo, Int)]
f :: Foo -> Int -> Bar
uncurry :: (a -> b -> c) -> (a, b) -> c
uncurry f :: (Foo, Int) -> Bar
map (uncurry f) (zip list [0..]) :: [Bar]
In JavaScript the uncurry function is simply apply. It is implemented as follows:
function uncurry(f, context) {
if (arguments.length < 2) context = null;
return function (args) {
return f.apply(context, args);
};
}
Using uncurry we can write the above example as:
var ys = map(uncurry(function (item, index) {
return item + index;
}), decorateIndices(xs));
This code is awesome because:
Each function does only one job. Functions can be combined to do more complex work.
Everything is explicit, which is a good thing according to the Zen of Python.
There's no redundancy. There's only one map function, etc.
So I really hope this answer helps.