I'm a newbie with Underscore.js.
Recently I read the documentation about reduce and reduceRight but I couldn't understand what is the difference between the two.
I will appreciate any help and example.
Well, _.reduce iterates over the collection starting at the first index and finishing on the last index while _.reduceRight does the same thing but starts at the last index and finishes on the first index.
var list = ['a', 'b', 'c'];
_.reduce(list, function(memo, item) { memo.push(item); return memo; }, []);
=> ['a', 'b', 'c']
_.reduceRight(list, function(memo, item) { memo.push(item); return memo; }, []);
=> ['c', 'b', 'a']
Related
How to easily assert nested array elements with another array values? I tried the following but it's not working as expected.
var expectedCells = ['a', 'b', 'c', 'd']
var actual = [['a'], ['b', 'e'], ['e'], ['c']] //['b', 'e'] should also return true
actual.forEach(element => console.log(expectedCells.includes(element)));
//Expected: true, true, false, true but it returns false, false, false, false
You can use Array#some to check if any of the array elements are in the other array.
var expectedCells = ['a', 'b', 'c', 'd']
var actual = [['a'], ['b', 'e'], ['e'], ['c']] //['b', 'e'] should also return true
actual.forEach(element => console.log(element.some(x => expectedCells.includes(x))));
expectedCells.includes(element) checks, if a nested array of actual is in the expectedCells array, which only contains strings, so the output is always false
What you need to do is to also iterate over the elements of the nested array.
var expectedCells = ['a', 'b', 'c', 'd']
var actual = [['a'], ['b', 'e'], ['e'], ['c']] //['b', 'e'] should also return true
actual.forEach(elements => {
for (let i=0; i<elements.length; i++) {
if (expectedCells.includes(elements[i])) {
return console.log(true)
}
return console.log(false)
}
});
There's a couple of things going on here.
First, you aren't comparing the elements inside of actual's subarrays. You're comparing the subarrays themselves.
actual.forEach(...) is executing something like this:
console.log(expectedCells.includes(['a']))
console.log(expectedCells.includes(['b', 'e']))
console.log(expectedCells.includes(['e']))
console.log(expectedCells.includes(['c']))
None of the array objects will match the letters a, b, c, or d from the expectedCells array.
You'll have to create a double loop:
actual.forEach(
elementArray =>
console.log(
elementArray.some(element => expected.includes(element))
)
)
I like to use Array.prototype.reduce() for several different scenarios in my code, it's pretty straightforward and handy.
Please observe in the 2 different solutions below the reduce() function takes 2 different initial values, in the first case is new Set() and the second is [].
In the below example the code uses reduce() without return keyword - Set one:
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = data.reduce((a, c) => a.add(c), new Set());
console.log(Array.from(result));
The next example is using still reduce() but here with a return keyword - Array one:
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = data.reduce((a, c) => {
a.find(e => e === c) ? null : a.push(c);
return a;
}, []);
console.log(result);
Question:
So the .add() function for Set returns the Set object itself. The .push() function for Array returns the length of the used Array.
The Set case helps me to shorten the code using .reduce() without return keyword because the above mentioned reason. In my second example I would like to use the solution without return keyword but still with Array somehow.
Is there any workaround solution to get the same result but without using return keyword in the second example? I would like to shorten the code further if possible.
Any help is appreciated.
You could take either the accumulator, if found or concat the element to the accumulator.
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = data.reduce((a, c) => a.find(e => e === c) ? a : a.concat(c), []);
console.log(result);
Just to mention, Set takes a complete array with the constructor.
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = Array.from(new Set(data));
console.log(result);
Array#concat can add a new item to an array and returns a new array, so can works similar to Set#add. However, it still needs the conditional operator since you want to either add an element or nothing - for the latter case that's concatenating an array with an empty array:
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = data.reduce((a, c) => a.concat(a.some(e => e === c) ? [] : c), []);
console.log(result);
Alternatively, you can use spread syntax to again combine two arrays:
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = data.reduce((a, c) => [...a, ...(a.some(e => e === c) ? [] : c)], []);
console.log(result);
Neither of the two is perfect, to be honest. The existence of the conditional operator makes this harder to read when all one line but it's still an option.
So I wanted the elements of the array arr1 that also happen to belong to the array arr2. I figured arr1.filter(arr2.includes) should do the trick, but it gave me an error (see below). Strangely, though, arr1.filter(x => arr2.incudes(x)) worked fine. Even though the functions arr2.includes and x => arr2.includes(x) aren't referentially equal, shouldn't they take the same values on the same inputs? What am I missing, here?
> arr1 = ['a', 'b', 'c']
[ 'a', 'b', 'c' ]
> arr2 = ['a', 'c', 'd']
[ 'a', 'c', 'd' ]
>
> arr1.filter(x => arr2.includes(x))
[ 'a', 'c' ]
> arr1.filter(arr2.includes)
TypeError: Cannot convert undefined or null to object
at includes (<anonymous>)
at Array.filter (native)
at repl:1:6
... etc ...
There are two reasons you can't just do arr1.filter(arr2.includes):
arr2.includes is just a reference to the function, but what you need is both a reference to the function and to the array that you want to use it on (arr2). You could solve that by using Function.prototype.bind, but:
filter passes its callback multiple arguments, not just one: It passes the value, its index, and the original array. includes will try to use the second argument it receives as the index at which to start searching, so when filter passes it the index, it'll use that and skip leading entries.
So the usual solution is to use a wrapper function that knows it needs to use includes on arr2 and knows to only pass it the one argument — which is what you've done with your arrow function.
But see also Michał Perłakowski's answer for an answer from the functional programming perspective using a utility function to create the callback function rather than creating it inline.
Here's how you could implement an includes function that could be used in point-free style:
const arr1 = ['a', 'b', 'c'];
const arr2 = ['a', 'c', 'd'];
const includes = arr => x => arr.includes(x);
console.log(arr1.filter(includes(arr2)));
If you're interested in functional programming in JavaScript, you should try the Ramda library. With Ramda your code could look like this:
const arr1 = ['a', 'b', 'c'];
const arr2 = ['a', 'c', 'd'];
// First option: R.flip
console.log(R.filter(R.flip(R.contains)(arr1), arr2));
// Second option: R.__ (placeholder argument)
console.log(R.filter(R.contains(R.__, arr1), arr2));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.24.1/ramda.min.js"></script>
I am going through a lesson on Javascript Arrays where we have to understand certain what is under the hood of underscoreJS methods. I need to write a function for the _.each method which will allow me to iterate over a single array and return a modified nested array that includes the index for each value.
For example:
var myArray = ['a', 'b', 'c'];
after the method each is called on myArray the new array should look like:
myArray = [ [ 'a', 0 ], [ 'b', 1 ], [ 'c', 2 ] ];
I have been searching on google for a day and have not found anything specific to this task. Stuck and need some help! Thank you.
_.each iterates over your array and has the following signature
function(value, index, array)
So lets see how it could be done ...
var result = [];
_.each(myArray, function(value, index, array){
result.push([value, index]); // <= does the work
}
This is not the ideal way (you should use map) but does illustrate how the each works.
Good luck
You can do it like below by using Array.prototype.reduce() function,
var myArray = ['a', 'b', 'c'];
var result = myArray.reduce(function(a,b,i){
return (a.push([b,i]), a)
},[]);
console.log(result); // [['a',0],['b',1],['c',2]];
Or as #bergi said you can do it with Array.prototype.map() also,
var myArray = ['a', 'b', 'c'];
var result = myArray.map(function(itm,i){
return [itm,i];
});
console.log(result); // [['a',0],['b',1],['c',2]];
Just use Array#map():
The map() method creates a new array with the results of calling a provided function on every element in this array.
var myArray = ['a', 'b', 'c'],
result = myArray.map(function (a, i) {
return [a, i];
});
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
If you're learning _.each method, you can do it like this
var myArray = ['a', 'b', 'c'];
_.each(myArray, function (e, i) {
myArray[i] = [e, i];
});
document.write(JSON.stringify(myArray));
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
But for your case better to use _.map function
Assuming we have:
array1 = ['A', 'B', 'C', 'D', 'E']; array2 = ['C', 'E'];
Is there a proven and fast solution to compare two arrays against each other, returning one array without the values appearing in both arrays (C and E here).
So:
array3 = ['A', 'B', 'D']
should be the output of the solution. (jquery may be involved)
thx.
I accepted Matthews Solution, but dont want to ignore a different faster solution i just found.
var list1 = [1, 2, 3, 4, 5, 6];
var list2 = ['a', 'b', 'c', 3, 'd', 'e'];
var lookup = {};
for (var j in list2) {
lookup[list2[j]] = list2[j];
}
for (var i in list1) {
if (typeof lookup[list1[i]] != 'undefined') {
alert('found ' + list1[i] + ' in both lists');
break;
}
}
Source: Optimize Loops to Compare Two Arrays
This is a set difference. A simple implementation is:
jQuery.grep(array1, function(el)
{
return jQuery.inArray(el, array2) == -1;
});
This is O(m * n), where those are the sizes of the arrays. You can do it in O(m + n), but you need to use some kind of hash set. You can use a JavaScript object as a simple hash set for strings. For relatively small arrays, the above should be fine.
a proven fast solution that i know of is a binary search that you can use after you sort one of the arrays. so the solution takes time that depends on the sorting algorithm. but is at least log(N).