Related
I don't understand why the followings provide different results, and on the second one I see people using an underscore (_, i), but I am not really sure what it does.
let test1 = Array.from(5, (v,i) => i); //empty array;
let test2 = Array.from({length:5}, (v,i) => i); // [0,1,2,3,4]
I don't get why I need to pass an object to get an array populated of the first n numbers.
Could someone please explain?
Let's see the signature of Array.from() method first.
Array.from(arrayLike, mapFn)
So, the first argument should be an arrayLike object (an object that has a length property of a non-negative integer). You can pass an array, string, etc. as well.
The second parameter is a map function which maps the the elements of the first argument (arrayLike) to the returned value from the callback function.
This map function takes a callback function with following signature.
callback(value, index)
The map function creates a new array populated with the results of calling the callback function (v, i) => i on every element in first argument of Array.from.
Now, let's see first case,
let test1 = Array.from(5, (v,i) => i);
Here, (v, i) => i is a map function. v is element of arrayLike object and i is the index of that element. The index i is returned.
But, 5 is neither an array nor an arrayLike object and the length property of 5 is undefined. So, map function maps no (zero) element and produces an empty array. Think of the following implementation.
function mapFn (arrayLike, callback) {
let result = [];
for (let i = 0; i < arrayLike.length; i++) {
let mappedValue = callback(arrayLike[i], i);
result.push(mappedValue);
}
return result;
}
If you pass 5 to the mapFn function, the for loop iterates 0 times and mapFn returns an empty array.
In case of second example,
let test2 = Array.from({length:5}, (v,i) => i); // [0,1,2,3,4]
{length: 5} is an arrayLike object. The map function produces an array with returned values of the callback function. The callback function returns index. Hence, [0,1,2,3,4].
In the callback function only the i parameter is used and returned from the function. v argument is not used. But, it needs to be there so that the second parameter i can be accessed. So, (_, i) is same as (v, i). You can name the parameters with any valid variable name.
You are basically following the following approach
Array.from(arrayLike, (currentValue, index) => { ... } )
So the arrayLike variable needs to be either a string, map ... or it should be an object determining the length of the array (like in your case). So in your case the size of the array is determined by the arrayLike object. currentvalue is undefined every time the arrow function runs and the index goes from 0 to arrayLike.length - 1.
Because it is expecting and arrayLike object and arrays have a length property.
const validateCred = arr => {
let checkableArr = arr.pop();
for (let i = arr.length - 1; i >= 0; i--) {
arr[i]
checkableArr.push(arr[i])
}
}
When i run the code, I get an error saying that .push() is not a function that I can use on checkableArr. this is because checkableArr isn't an array due to it being a variation of arr (the argument that will be passed when the function is called), which the function isn't sure is an array, is there any way to check that the argument passed into the function is an array?
EDIT:
The thing I was looking for is called isArray(), a method that returns a boolean indicating if the item passed into it is an array or no. Thanks to #David for showing me this tool, along with a bunch of helpful information that helped a lot with writing my program
You're getting that error, because you haven't made sure that the last item of the passed array (arr) is an array itself, but your function's logic requires it to be an array.
There are various ways to solve this, some of them have already been outlined by others (#hungerstar).
Check the last element of arr
One attempt is to ensure that the last element/item inside arr is an array and bail out if it isn't.
const validateCred = arr => {
let lastItem = arr.pop();
if (!Array.isArray(lastItem)) {
throw new Error('validateCred :: Last item of passed array must be an array itself');
}
// ... rest of your code ...
}
Although that does not solve the root cause, it ensures you get a decent and descriptive message about what went wrong. It's possible to improve that by defining a fallback array in case the last item isn't an array itself. Something like this:
const validateCred = arr => {
let lastItem = arr.pop();
let checkableArr = Array.isArray(lastItem) ? lastItem : [];
// ... rest of your code ...
}
One thing to note: If the last item may be an array with a value inside, you have to copy that value into the new array!
const validateCred = arr => {
let lastItem = arr.pop();
let checkableArr = Array.isArray(lastItem) ? lastItem : [lastItem]; // <--
// ... rest of your code ...
}
HINT: The following answer is based on guessing. The name validateCred lets me assume you use it to validate credentials. However, that's just guessing because all the provided code does is taking the last item and then pushing the rest of the contents of arr reversely into it (= reversing and flattening)
Reversing and flattening
If all you want to do with validateCred is reversing and flattening (and you only target supporting environments), you can easily do that with a one-liner:
// impure version
const validateCred = arr => arr.reverse().flat();
// pure version
const validateCred = arr => arr.flat().reverse();
To support older environments as well, you can use .reduce and .concat instead of .flat:
// impure version
const validateCred = arr => arr.reverse().reduce((acc, x) => acc.concat(x), []);
// pure version
const validateCred = arr => arr.reduce((acc, x) => acc.concat(x), []).reverse();
Yes, we pass an array as an argument using call/apply method. In your code when you are using arr.pop() it gets converted to number/string depending upon what type of array you specified, I specified below integer value so checkableArr is now integer so because of this you are getting an error.
Corrected code is here. Just replace in your code like:
let checkableArr = arr; //arr.pop();
I am just wondering why it is not possible to make forEach on array of undefined.
Code:
var arr = new Array(5); // [undefined x 5]
//ES5 forEach
arr.forEach(function(elem, index, array) {
console.log(index);
});
//underscore each
_.each(arr, function(elem, index, array) {
console.log(index);
});
Both examples do not execute function.
Now to make foreach, I have to make:
var arr = [0,0,0,0,0];
Then make forEach on it.
I am trying to make an array with specified size and loop through it, avoiding for loop. I think that it is clearer using forEach than for loop.
With array with length 5 it is not a problem, but it would be ugly with bigger arrays.
Why there is a problem looping through array of undefined values ?
Array(5) is essentialy equivalent to
var arr = [];
arr.length = 5;
In javascript changing array's length doesn't set any values for it's numeric properties nor does it define those properties in the array object. So numeric properties are undefined instead of having undefined value. You can check it by using:
Object.keys(arr)
When iterating javascript iterates through numeric properties of the array, so if these don't exist, there is nothing to iterate over.
You can check it by doing:
var arr = Array(5)
//prints nothing
arr.forEach(function(elem, index, array) {
console.log(index);
});
arr[3] = 'hey'
//prints only 'hey' even though arr.length === 5
arr.forEach(function(elem, index, array) {
console.log(index);
});
The following code:
var arr = [undefined, undefined];
creates and array of length ===2 and sets the both numeric properties 0 and 1 to undefined.
Looking at a simplified implementation of .forEach() may help.
Array.prototype.my_for_each = function(callback, thisArg) {
for (var i = 0; i < this.length; i++) {
if (i in this) {
callback.call(thisArg, this[i], i, this);
}
}
};
So you can see that what happens is that the method does iterate the entire Array (according to the spec), but it only invokes the callback if the member actually exists. It checks by looking for the property (the index) using the in operator, which tests to see if the object either has or inherits the property.
If in shows that the index exists, the callback is invoked.
So given this Array:
var arr = ["foo", "bar", "baz"];
This will output all 3 items:
arr.my_for_each(function(item) {
console.log(item);
});
// "foo" "bar" "baz"
But if we use delete to remove a member, it leaves a hole in the Array, which will now get passed over.
delete arr[1];
arr.my_for_each(function(item) {
console.log(item);
});
// "foo" "baz"
When you created an Array using Array(5), it created one without any members, but with the .length set to 5. So this is an example of a sparse Array (very sparse in this instance). Because none of the indices will be found by in, the callback is never invoked.
You can use Array.from to create an array and pass lambda function that will be invoked on each item in the array.
detailed documentation
const arr = Array.from(
{ length: 5 },
() => 0
)
console.log(arr)
Other answers have explained the problem, but not provided solutions.
ES6 Spread syntax fills in sparse values with undefined. So does Array.apply(null, sparseArray), which has the benefit of working in older browsers, but takes a bit more work to understand.
const sparseArray = new Array(5);
const unsparseArray1 = Array.apply(null, sparseArray);
const unsparseArray2 = [...sparseArray];
function arrayApply() {
// ES5 forEach works with either approach
unsparseArray1.forEach(function(elem, index, array) {
console.log(index);
});
}
function es6Spread() {
// Lodash each works with either approach
_.each(unsparseArray2, function(elem, index, array) {
console.log(index);
});
}
<html><head>
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.21/lodash.min.js"></script>
<title>Making sparse arrays unsparse</title>
</head><body>
<p><button onclick="arrayApply();">Array.apply(null, sparseArray)</button></p>
<p><button onclick="es6Spread();">[...sparseArray]</button></p>
</body>
</html>
In my case, I was looking for an elegant solution to creating an array of digits starting with 0 to X.
In an elegant manner with arrow functions, it comes up with a 1 line of code
const arrayLength = 10;
const arrayOfDigits = Array.apply(null, Array(arrayLength)).map((_, index) => index);
Appeared to me quite a sophisticated one, much more than a separate block of code with a for cycle.
It should be quite easy to implement array.map() that is defined in ECMA-262, which takes a function and this function will be called by 3 arguments: element value, index, the array.
But what about for sparse array? Obviously we don't want to iterate from index 0 to 100,000 if only index 0, 1, 2, and 100,000 has an element and otherwise is sparse from index 3 to 99,999. I can think of using arr.slice(0) or arr.concat() to clone the array, and then put in the replaced values, but what if we don't use slice or concat, is there another way to do it?
The solution I came up with using slice() is:
Array.prototype.collect = Array.prototype.collect || function(fn) {
var result = this.slice(0);
for (var i in this) {
if (this.hasOwnProperty(i))
result[i] = fn(this[i], i, this); // 3 arguments according to ECMA specs
}
return result;
};
(collect is used to try out the code, as that's another name for map in some language)
It should be easy, but there are a few peculiar points.
The callback function is allowed to modify the array in question. Any elements it adds or removes are not visited. So it seems we should use something like Object.keys to determine which elements to visit.
Also, the result is defined to be a new array "created as if by" the array constructor taking the length of the old array, so we might as well use that constructor to create it.
Here's an implementation taking these things into account, but probably missing some other subtleties:
function map(callbackfn, thisArg) {
var keys = Object.keys(this),
result = new Array(this.length);
keys.forEach(function(key) {
if (key >= 0 && this.hasOwnProperty(key)) {
result[key] = callbackfn.call(thisArg, this[key], key, this);
}
}, this);
return result;
}
I am assuming Object.keys returns the keys of the array in numerical order, which I think is implementation defined. If it doesn't, you could sort them.
You don't need to use this.slice(0). You can just make result an array and assign values to any index:
Array.prototype.collect = Array.prototype.collect || function(fn) {
var result = [];
for(var i in this) {
if (this.hasOwnProperty(i)) {
result[i] = fn(this[i]);
}
}
return result;
}
I would like to filter an array of items by using the map() function. Here is a code snippet:
var filteredItems = items.map(function(item)
{
if( ...some condition... )
{
return item;
}
});
The problem is that filtered out items still uses space in the array and I would like to completely wipe them out.
Any idea?
EDIT: Thanks, I forgot about filter(), what I wanted is actually a filter() then a map().
EDIT2: Thanks for pointing that map() and filter() are not implemented in all browsers, although my specific code was not intended to run in a browser.
You should use the filter method rather than map unless you want to mutate the items in the array, in addition to filtering.
eg.
var filteredItems = items.filter(function(item)
{
return ...some condition...;
});
[Edit: Of course you could always do sourceArray.filter(...).map(...) to both filter and mutate]
Inspired by writing this answer, I ended up later expanding and writing a blog post going over this in careful detail. I recommend checking that out if you want to develop a deeper understanding of how to think about this problem--I try to explain it piece by piece, and also give a JSperf comparison at the end, going over speed considerations.
That said, **The tl;dr is this:
To accomplish what you're asking for (filtering and mapping within one function call), you would use Array.reduce()**.
However, the more readable and (less importantly) usually significantly faster2 approach is to just use filter and map chained together:
[1,2,3].filter(num => num > 2).map(num => num * 2)
What follows is a description of how Array.reduce() works, and how it can be used to accomplish filter and map in one iteration. Again, if this is too condensed, I highly recommend seeing the blog post linked above, which is a much more friendly intro with clear examples and progression.
You give reduce an argument that is a (usually anonymous) function.
That anonymous function takes two parameters--one (like the anonymous functions passed in to map/filter/forEach) is the iteratee to be operated on. There is another argument for the anonymous function passed to reduce, however, that those functions do not accept, and that is the value that will be passed along between function calls, often referred to as the memo.
Note that while Array.filter() takes only one argument (a function), Array.reduce() also takes an important (though optional) second argument: an initial value for 'memo' that will be passed into that anonymous function as its first argument, and subsequently can be mutated and passed along between function calls. (If it is not supplied, then 'memo' in the first anonymous function call will by default be the first iteratee, and the 'iteratee' argument will actually be the second value in the array)
In our case, we'll pass in an empty array to start, and then choose whether to inject our iteratee into our array or not based on our function--this is the filtering process.
Finally, we'll return our 'array in progress' on each anonymous function call, and reduce will take that return value and pass it as an argument (called memo) to its next function call.
This allows filter and map to happen in one iteration, cutting down our number of required iterations in half--just doing twice as much work each iteration, though, so nothing is really saved other than function calls, which are not so expensive in javascript.
For a more complete explanation, refer to MDN docs (or to my post referenced at the beginning of this answer).
Basic example of a Reduce call:
let array = [1,2,3];
const initialMemo = [];
array = array.reduce((memo, iteratee) => {
// if condition is our filter
if (iteratee > 1) {
// what happens inside the filter is the map
memo.push(iteratee * 2);
}
// this return value will be passed in as the 'memo' argument
// to the next call of this function, and this function will have
// every element passed into it at some point.
return memo;
}, initialMemo)
console.log(array) // [4,6], equivalent to [(2 * 2), (3 * 2)]
more succinct version:
[1,2,3].reduce((memo, value) => value > 1 ? memo.concat(value * 2) : memo, [])
Notice that the first iteratee was not greater than one, and so was filtered. Also note the initialMemo, named just to make its existence clear and draw attention to it. Once again, it is passed in as 'memo' to the first anonymous function call, and then the returned value of the anonymous function is passed in as the 'memo' argument to the next function.
Another example of the classic use case for memo would be returning the smallest or largest number in an array. Example:
[7,4,1,99,57,2,1,100].reduce((memo, val) => memo > val ? memo : val)
// ^this would return the largest number in the list.
An example of how to write your own reduce function (this often helps understanding functions like these, I find):
test_arr = [];
// we accept an anonymous function, and an optional 'initial memo' value.
test_arr.my_reducer = function(reduceFunc, initialMemo) {
// if we did not pass in a second argument, then our first memo value
// will be whatever is in index zero. (Otherwise, it will
// be that second argument.)
const initialMemoIsIndexZero = arguments.length < 2;
// here we use that logic to set the memo value accordingly.
let memo = initialMemoIsIndexZero ? this[0] : initialMemo;
// here we use that same boolean to decide whether the first
// value we pass in as iteratee is either the first or second
// element
const initialIteratee = initialMemoIsIndexZero ? 1 : 0;
for (var i = initialIteratee; i < this.length; i++) {
// memo is either the argument passed in above, or the
// first item in the list. initialIteratee is either the
// first item in the list, or the second item in the list.
memo = reduceFunc(memo, this[i]);
// or, more technically complete, give access to base array
// and index to the reducer as well:
// memo = reduceFunc(memo, this[i], i, this);
}
// after we've compressed the array into a single value,
// we return it.
return memo;
}
The real implementation allows access to things like the index, for example, but I hope this helps you get an uncomplicated feel for the gist of it.
That's not what map does. You really want Array.filter. Or if you really want to remove the elements from the original list, you're going to need to do it imperatively with a for loop.
Array Filter method
var arr = [1, 2, 3]
// ES5 syntax
arr = arr.filter(function(item){ return item != 3 })
// ES2015 syntax
arr = arr.filter(item => item != 3)
console.log( arr )
You must note however that the Array.filter is not supported in all browser so, you must to prototyped:
//This prototype is provided by the Mozilla foundation and
//is distributed under the MIT license.
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
if (!Array.prototype.filter)
{
Array.prototype.filter = function(fun /*, thisp*/)
{
var len = this.length;
if (typeof fun != "function")
throw new TypeError();
var res = new Array();
var thisp = arguments[1];
for (var i = 0; i < len; i++)
{
if (i in this)
{
var val = this[i]; // in case fun mutates this
if (fun.call(thisp, val, i, this))
res.push(val);
}
}
return res;
};
}
And doing so, you can prototype any method you may need.
TLDR: Use map (returning undefined when needed) and then filter.
First, I believe that a map + filter function is useful since you don't want to repeat a computation in both. Swift originally called this function flatMap but then renamed it to compactMap.
For example, if we don't have a compactMap function, we might end up with computation defined twice:
let array = [1, 2, 3, 4, 5, 6, 7, 8];
let mapped = array
.filter(x => {
let computation = x / 2 + 1;
let isIncluded = computation % 2 === 0;
return isIncluded;
})
.map(x => {
let computation = x / 2 + 1;
return `${x} is included because ${computation} is even`
})
// Output: [2 is included because 2 is even, 6 is included because 4 is even]
Thus compactMap would be useful to reduce duplicate code.
A really simple way to do something similar to compactMap is to:
Map on real values or undefined.
Filter out all the undefined values.
This of course relies on you never needing to return undefined values as part of your original map function.
Example:
let array = [1, 2, 3, 4, 5, 6, 7, 8];
let mapped = array
.map(x => {
let computation = x / 2 + 1;
let isIncluded = computation % 2 === 0;
if (isIncluded) {
return `${x} is included because ${computation} is even`
} else {
return undefined
}
})
.filter(x => typeof x !== "undefined")
I just wrote array intersection that correctly handles also duplicates
https://gist.github.com/gkucmierz/8ee04544fa842411f7553ef66ac2fcf0
// array intersection that correctly handles also duplicates
const intersection = (a1, a2) => {
const cnt = new Map();
a2.map(el => cnt[el] = el in cnt ? cnt[el] + 1 : 1);
return a1.filter(el => el in cnt && 0 < cnt[el]--);
};
const l = console.log;
l(intersection('1234'.split``, '3456'.split``)); // [ '3', '4' ]
l(intersection('12344'.split``, '3456'.split``)); // [ '3', '4' ]
l(intersection('1234'.split``, '33456'.split``)); // [ '3', '4' ]
l(intersection('12334'.split``, '33456'.split``)); // [ '3', '3', '4' ]
First you can use map and with chaining you can use filter
state.map(item => {
if(item.id === action.item.id){
return {
id : action.item.id,
name : item.name,
price: item.price,
quantity : item.quantity-1
}
}else{
return item;
}
}).filter(item => {
if(item.quantity <= 0){
return false;
}else{
return true;
}
});
following statement cleans object using map function.
var arraytoclean = [{v:65, toberemoved:"gronf"}, {v:12, toberemoved:null}, {v:4}];
arraytoclean.map((x,i)=>x.toberemoved=undefined);
console.dir(arraytoclean);