can any one explain reduce function in javascripts - javascript

Can any one explain whats going on in this JavaScript code? I do not understand the part where the i.reduce is passed with [] as an initial value:
function longestString(i) {
// It will be an array like (['big',[0,1,2,3,4],'tiny'])
// and the function should return the longest string in the array
// This should flatten an array of arrays
var r = i.reduce(function(a, b) {
return a.concat(b);
}, []);
// This should fetch the longest in the flattened array
return r.reduce(function (a, b)
{
return a.length > b.length ? a : b;
});
}

The initial value in a reduce is an accumulator. If for example if i is [[1],[2],[3]] then the reduce statement is equivalent to:
r = [];
r = r.concat([1]);
r = r.concat([2]);
r = r.concat([3]);
In each step of the reduce the function must be called on two arguments. In the first step there must be some initial value. You can't call .concat on nothing so you start with an empty array.

Related

Question about recursive JS - from freecodecamp

There is this function:
function countup(n) {
if (n < 1) {
return [];
} else {
const countArray = countup(n - 1);
countArray.push(n);
return countArray;
}
}
console.log(countup(5));
How is [] being pushed first? From the code, it looks like it is being counted down from n. Can someone explain?
Bonus question: Why write like this when I can write:
function countup(n){
let answer = []
for (let i = 1; i <= n; i++){
(i<n) ? answer.push(i)
: answer.push(n)
}
return answer
}
console.log(countup(5));
which is a shorter function. thank you in advance!
Even though the numbers are in descending order starting from n, they are actually being pushed smallest number first. This is because before the push() operation can happen we keep going down in the recursion stack when we call countup(n - 1) every layer of the recursion. This is why we reach a point where n < 1 i.e. 0 where an empty array is returned.
Next, we encounter the first array.push() operation and so the first number that is pushed into the array is actually 1 and not n. Next, the recursion layers start winding up and we push numbers 2, 3, and so on till n.
Yes you can achieve the same using a traditional for loop, but I guess they just want to explain how recursion works.
To understand recursions, sometimes you need to follow the execution of the code manually. Now the following line causes the recursive call and subtracts one from the number and then passes it to the recursive call.
const countArray = countup(n - 1);
Now imagine, if you passed 5 to the initial function call like so:
countup(5);
As 5 is not less than 1, the else block will be executed. The first line in the else block is the recursive call. So when your program encounters the recursive call, it stops execution of the current function until that recursive function call is concluded/returned. So the first recursive call will be:
const countArray = countup(4);
consequently, the same process will be repeated until n equals 0. As 0 is smaller than 1, the if condition will evaluate to true. and it will return an empty array.
After the recursive call, it pushes the number to the array returned by recursive call. so the array at that point will contain [0] and then
[0, 1] and it will keep adding numbers till the all recursive calls have been computed and then it will execute the rest of the intial function call.
Often times, recursion can be replaced by loops but thats not the case always. As a programmer one should be aware of this important topic.
Since countup() is fully divided by an if statement that depends on n, let's first see what exectutes for different vales of n.
To make it a lot easier to follow, we'll append our current n to countArray's variable name:
n == 5:
const countArray5 = countup(4);
countArray5.push(5);
return countArray5;
⋮
n == 1:
const countArray1 = countup(0);
countArray1.push(1);
return countArray1;
n == 0:
return [];
Now let's sequentially expand each inner call to countup(n):
countup(5) {
const countArray5 = countup(4);
countArray5.push(5);
return countArray5;
}
countup(5) {
const countArray5 = countup(4) {
const countArray4 = countup(3);
countArray4.push(4);
return countArray4;
};
countArray5.push(5);
return countArray5;
}
Expanding until countup(0):
countup(5) {
const countArray5 = countup(4) {
const countArray4 = countup(3) {
const countArray3 = countup(2) {
const countArray2 = countup(1) {
const countArray1 = countup(0) // countup(0) returns [] so countArray1 = [];
countArray1.push(1); // Then push '1' to countArray1;
return countArray1; // Return [1] at counterArray2 = countup(1) above
};
countArray2.push(2); // countArray2 was [1], now push '2' to make it [1,2]
return countArray2; // Return [1,2] at countArray3 = countup(2) above
};
countArray3.push(3); // Push '3' to countArray3 => [1,2,3]
return countArray3; // return [1,2,3] at countArray4 = countup(3)
};
countArray4.push(4); // Push '4' to countArray4 => [1,2,3,4]
return countArray4; // Return [1,2,3,4] at countArray5 = countup(4)
};
countArray5.push(5); // Push '5' to countArray5 => [1,2,3,4,5]
return countArray5; // countup(5) returns [1,2,3,4,5]
}

How to return a multidimensional array of numbers in descending order?

I am new to JS and I am tring to sort a multidimensional array and I want to return the array in descending order -
Input -
let input = [[1,2,3],[1,3,2],[3,2,1],[3,1,2],[2,1,3],[2,3,1]]
Expected Ouput
[[3,2,1],[3,1,2],[2,3,1],[2,1,3],[1,3,2],[1,2,3]]
I have tried sort
let result = input.sort((a, b) => a - b)
But I am getting the same array sent back to me
[[1,2,3],[1,3,2],[3,2,1],[3,1,2],[2,1,3],[2,3,1]]
I have also tried a for loop with the sort method
for(let i = 0; i < input.length; i++){
let inputArr = input[i]
let output = inputArr.sort((a,b) => a - b)
console.log(output)
}
But I get returned
[1,2,3] 6 times (the length of the original array?)
How do I return the array values in descending order?
Thanks
You need to compare the subarray items against each other - .sort((a, b) -> a - b) makes no sense because a and b are arrays, and so can't be meaningfully subtracted from each other.
let input = [[1,2,3],[1,3,2],[3,2,1],[3,1,2],[2,1,3],[2,3,1]];
input.sort((a, b) => {
// Find the first index that's different between the two subarrays being compared
const diffIndex = a.findIndex((itemA, i) => b[i] !== itemA);
// Return the difference, so that the higher value will come first in the result
// If no difference found, return 0 (so they will come next to each other)
return diffIndex === -1 ? 0 : b[diffIndex] - a[diffIndex];
});
console.log(input);
That's assuming that the subarrays contain the same number of values, as it is in the example.
The sort operation expects numbers. You can apply the .sort() array method on input using concatenated inner array elements converted into a number - +arr.join('') - as in the following demo:
let input = [ [1,2,3], [1,3,2], [3,2,1], [3,1,2], [2,1,3], [2,3,1] ];
const sorted = input.sort( (a,b) => +b.join('') - +a.join('') );
console.log( sorted );
//OUTPUT: [ [3,2,1], [3,1,2], [2,3,1], [2,1,3], [1,3,2], [1,2,3] ]
NOTE
You may also use parseInt( arr.join('') ) in place of +arr.join('').

Cartesian product for arrays in 2Darray

I'm trying to make a Cartesian product between sub-arrays of a 2D array.
My 2dArray is for ex like this: var matrix = [[1,2,3], [4,5], [6,7,8],[9,10,11]];, but it can have variable number of arrays.
The function that i use is:
function cartesianProductOf() {
return Array.prototype.reduce.call(arguments, function(a, b) {
var ret = [];
a.forEach(function(a) {
b.forEach(function(b) {
ret.push(a.concat([b]));
});
});
return ret;
}, [[]]);
}
I tried to use the function like: cartesianProductOf(matrix) but this does not return any result.
My problem is how to give sub-arrays as parameter for this function? How to split the 2d array in sub-arrays or how to modify the function to work for my case?
Does anyone know how to solve this problem?
Update:
http://jsfiddle.net/XHEJt/10/
var matrix = [[1,2,3], [4,5], [6,7,8],[9,10,11]];
console.log(cartesianProductOf([1,2,3], [4,5], [6,7,8],[9,10,11]));
//the following line does not produce the same output
console.log(cartesianProductOf(matrix));
function cartesianProductOf() {
return Array.prototype.reduce.call(arguments, function(a, b) {
var ret = [];
a.forEach(function(a) {
b.forEach(function(b) {
ret.push(a.concat([b]));
});
});
return ret;
}, [[]]);
}
While you are asking about a function call with parameters (which works) and with an array (which does not work), you could insert a check, if arguments.length is equal to one, and then take the first element of arguments as value for reducing.
return Array.prototype.reduce.call(arguments.length === 1
? arguments[0]
: arguments, function(a, b) {
// ...

Using Map-Reduce to Return A Modified Array of Strings

I am learning map & reduce, but am having a hard time understanding how to utilize these methods to tackle problems.
For example,
Create a function that takes a number and returns an array of strings containing the number cut off at each digit.
420 should return ["4", "42", "420"]
My old Approach:
function createArrayOfTiers(num) {
var numArr = num.toString().split('');
var output = [];
for(var i = numArr.length-1; i>=0; i--) {
output.unshift(numArr.join('');
numArr.pop();
}
return output;
}
Attempt to use map-reduce combination:
function createArrayOfTiers(num) {
var numArr = num.toString().split('');
return numArr.map(function(element) {
var newElement = numArr.reduce(function(acc, val) {
return acc + val;
});
numArr.splice(element, 1);
return newElement;
});
}
You have used two loops, but apparently it can be done just with one.
function n(num) {
let res = (""+num).split('').map((_,i) => (""+num).slice(0, i+1));
return res;
}
console.log(n(420));
console.log(n(13579));
One-liner.
const n = num => (""+num).split('').map((_,i) => (""+num).slice(0, i+1));
console.log(n(420));
console.log(n(13579));
As others noted, that this problem doesn't seem to be the best use case of the map and reduce functions.
map function provides the element, index and array information in the parameters. Making use of these you can iterate on the elements you need to apply the reduce function.
Statement var arrayToIterate = arr.slice(0,i+1); helps to achieve the above mentioned array to iterate.
Complete Code:
function createArrayOfTiers(num) {
var numArr = num.toString().split('');
return numArr.map(function(element, i, arr) {
var arrayToIterate = arr.slice(0,i+1);
var newElement = arrayToIterate.reduce(function(acc, val) {
return acc + val;
},"");
return newElement;
});
}
var result = createArrayOfTiers(420);
console.log(result);
I don't think these are good uses-cases of map or reduce, but here goes :
var numArr = [4,2,0];
var result = numArr.map(e => numArr.join('')) // now ["420", "420", "420"]
.map((arr, i) => arr.substring(0, i+1)) // now ["4", "42", "420"]
console.log(result);
We first replace every element of the array by the whole (unmodified) array joined into a string, then substring each of these strings based on their position in the outer array.
There's a reduction hidden in there, although not using reduce : join reduces [4, 2, 0] to "420".
I am learning map & reduce, but am having a hard time understanding how to utilize these methods to tackle problems.
Mapping associates to each value of the source array a new value provided by a mapping function : if you have an [x, y, z] array, a mapping function f(x)=x+1, the result of mapping the array with the function will be [x+1, y+1, z+1].
I believe reduction was meant to "reduce" an array to a primitive type, although I might be mistaken. If you have an [x, y, z] array and reduce it with the addition operation, the result will be x+y+z.

Javascript array sort and unique

I have a JavaScript array like this:
var myData=['237','124','255','124','366','255'];
I need the array elements to be unique and sorted:
myData[0]='124';
myData[1]='237';
myData[2]='255';
myData[3]='366';
Even though the members of array look like integers, they're not integers, since I have already converted each to be string:
var myData[0]=num.toString();
//...and so on.
Is there any way to do all of these tasks in JavaScript?
This is actually very simple. It is much easier to find unique values, if the values are sorted first:
function sort_unique(arr) {
if (arr.length === 0) return arr;
arr = arr.sort(function (a, b) { return a*1 - b*1; });
var ret = [arr[0]];
for (var i = 1; i < arr.length; i++) { //Start loop at 1: arr[0] can never be a duplicate
if (arr[i-1] !== arr[i]) {
ret.push(arr[i]);
}
}
return ret;
}
console.log(sort_unique(['237','124','255','124','366','255']));
//["124", "237", "255", "366"]
You can now achieve the result in just one line of code.
Using new Set to reduce the array to unique set of values.
Apply the sort method after to order the string values.
var myData=['237','124','255','124','366','255']
var uniqueAndSorted = [...new Set(myData)].sort()
UPDATED for newer methods introduced in JavaScript since time of question.
This might be adequate in circumstances where you can't define the function in advance (like in a bookmarklet):
myData.sort().filter(function(el,i,a){return i===a.indexOf(el)})
Here's my (more modern) approach using Array.protoype.reduce():
[2, 1, 2, 3].reduce((a, x) => a.includes(x) ? a : [...a, x], []).sort()
// returns [1, 2, 3]
Edit: More performant version as pointed out in the comments:
arr.sort().filter((x, i, a) => !i || x != a[i-1])
function sort_unique(arr) {
return arr.sort().filter(function(el,i,a) {
return (i==a.indexOf(el));
});
}
How about:
array.sort().filter(function(elem, index, arr) {
return index == arr.length - 1 || arr[index + 1] != elem
})
This is similar to #loostro answer but instead of using indexOf which will reiterate the array for each element to verify that is the first found, it just checks that the next element is different than the current.
Try using an external library like underscore
var f = _.compose(_.uniq, function(array) {
return _.sortBy(array, _.identity);
});
var sortedUnique = f(array);
This relies on _.compose, _.uniq, _.sortBy, _.identity
See live example
What is it doing?
We want a function that takes an array and then returns a sorted array with the non-unique entries removed. This function needs to do two things, sorting and making the array unique.
This is a good job for composition, so we compose the unique & sort function together. _.uniq can just be applied on the array with one argument so it's just passed to _.compose
the _.sortBy function needs a sorting conditional functional. it expects a function that returns a value and the array will be sorted on that value. Since the value that we are ordering it by is the value in the array we can just pass the _.identity function.
We now have a composition of a function that (takes an array and returns a unique array) and a function that (takes an array and returns a sorted array, sorted by their values).
We simply apply the composition on the array and we have our uniquely sorted array.
This function doesn't fail for more than two duplicates values:
function unique(arr) {
var a = [];
var l = arr.length;
for(var i=0; i<l; i++) {
for(var j=i+1; j<l; j++) {
// If a[i] is found later in the array
if (arr[i] === arr[j])
j = ++i;
}
a.push(arr[i]);
}
return a;
};
Here is a simple one liner with O(N), no complicated loops necessary.
> Object.keys(['a', 'b', 'a'].reduce((l, r) => l[r] = l, {})).sort()
[ 'a', 'b' ]
Explanation
Original data set, assume its coming in from an external function
const data = ['a', 'b', 'a']
We want to group all the values onto an object as keys as the method of deduplication. So we use reduce with an object as the default value:
[].reduce(fn, {})
The next step is to create a reduce function which will put the values in the array onto the object. The end result is an object with a unique set of keys.
const reduced = data.reduce((l, r) => l[r] = l, {})
We set l[r] = l because in javascript the value of the assignment expression is returned when an assignment statement is used as an expression. l is the accumulator object and r is the key value. You can also use Object.assign(l, { [r]: (l[r] || 0) + 1 }) or something similar to get the count of each value if that was important to you.
Next we want to get the keys of that object
const keys = Object.keys(reduced)
Then simply use the built-in sort
console.log(keys.sort())
Which is the set of unique values of the original array, sorted
['a', 'b']
The solution in a more elegant way.
var myData=['237','124','255','124','366','255'];
console.log(Array.from(new Set(myData)).sort((a,b) => a - b));
I know the question is very old, but maybe someone will come in handy
A way to use a custom sort function
//func has to return 0 in the case in which they are equal
sort_unique = function(arr,func) {
func = func || function (a, b) {
return a*1 - b*1;
};
arr = arr.sort(func);
var ret = [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (func(arr[i-1],arr[i]) != 0)
ret.push(arr[i]);
}
}
return ret;
}
Example: desc order for an array of objects
MyArray = sort_unique(MyArray , function(a,b){
return b.iterator_internal*1 - a.iterator_internal*1;
});
No redundant "return" array, no ECMA5 built-ins (I'm pretty sure!) and simple to read.
function removeDuplicates(target_array) {
target_array.sort();
var i = 0;
while(i < target_array.length) {
if(target_array[i] === target_array[i+1]) {
target_array.splice(i+1,1);
}
else {
i += 1;
}
}
return target_array;
}
I guess I'll post this answer for some variety. This technique for purging duplicates is something I picked up on for a project in Flash I'm currently working on about a month or so ago.
What you do is make an object and fill it with both a key and a value utilizing each array item. Since duplicate keys are discarded, duplicates are removed.
var nums = [1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 9, 10];
var newNums = purgeArray(nums);
function purgeArray(ar)
{
var obj = {};
var temp = [];
for(var i=0;i<ar.length;i++)
{
obj[ar[i]] = ar[i];
}
for (var item in obj)
{
temp.push(obj[item]);
}
return temp;
}
There's already 5 other answers, so I don't see a need to post a sorting function.
// Another way, that does not rearrange the original Array
// and spends a little less time handling duplicates.
function uniqueSort(arr, sortby){
var A1= arr.slice();
A1= typeof sortby== 'function'? A1.sort(sortby): A1.sort();
var last= A1.shift(), next, A2= [last];
while(A1.length){
next= A1.shift();
while(next=== last) next= A1.shift();
if(next!=undefined){
A2[A2.length]= next;
last= next;
}
}
return A2;
}
var myData= ['237','124','255','124','366','255','100','1000'];
uniqueSort(myData,function(a,b){return a-b})
// the ordinary sort() returns the same array as the number sort here,
// but some strings of digits do not sort so nicely numerical.
function sort() only is only good if your number has same digit, example:
var myData = ["3","11","1","2"]
will return;
var myData = ["1","11","2","3"]
and here improvement for function from mrmonkington
myData.sort().sort(function(a,b){return a - b;}).filter(function(el,i,a){if(i==a.indexOf(el) & el.length>0)return 1;return 0;})
the above function will also delete empty array and you can checkout the demo below
http://jsbin.com/ahojip/2/edit
O[N^2] solutions are bad, especially when the data is already sorted, there is no need to do two nested loops for removing duplicates. One loop and comparing to the previous element will work great.
A simple solution with O[] of sort() would suffice. My solution is:
function sortUnique(arr, compareFunction) {
let sorted = arr.sort(compareFunction);
let result = sorted.filter(compareFunction
? function(val, i, a) { return (i == 0 || compareFunction(a[i-1], val) != 0); }
: function(val, i, a) { return (i == 0 || a[i-1] !== val); }
);
return result;
}
BTW, can do something like this to have Array.sortUnique() method:
Array.prototype.sortUnique = function(compareFunction) {return sortUnique(this, compareFunction); }
Furthermore, sort() could be modified to remove second element if compare() function returns 0 (equal elements), though that code can become messy (need to revise loop boundaries in the flight). Besides, I stay away from making my own sort() functions in interpreted languages, since it will most certainly degrade the performance. So this addition is for the ECMA 2019+ consideration.
The fastest and simpleness way to do this task.
const N = Math.pow(8, 8)
let data = Array.from({length: N}, () => Math.floor(Math.random() * N))
let newData = {}
let len = data.length
// the magic
while (len--) {
newData[data[len]] = true
}
var array = [2,5,4,2,5,9,4,2,6,9,0,5,4,7,8];
var unique_array = [...new Set(array)]; // [ 2, 5, 4, 9, 6, 0, 7, 8 ]
var uniqueWithSorted = unique_array.sort();
console.log(uniqueWithSorted);
output = [ 0, 2, 4, 5, 6, 7, 8, 9 ]
Here, we used only Set for removing duplicity from the array and then used sort for sorting array in ascending order.
I'm afraid you can't combine these functions, ie. you gotta do something like this:-
myData.unique().sort();
Alternatively you can implement a kind of sortedset (as available in other languages) - which carries both the notion of sorting and removing duplicates, as you require.
Hope this helps.
References:-
Array.sort
Array.unique

Categories

Resources