The javascript includes function can be used to find if an element is present in an array. Take the following example:
var arr = ['hello', 2, 4, [1, 2]];
console.log( arr.includes('hello') );
console.log( arr.includes(2) );
console.log( arr.includes(3) );
console.log( arr.includes([1, 2]) );
Passing 'hello' or 2 to the function returns true, as both are present in the array arr.
Passing 3 to the function returns false because it is not present in the array.
However, why does arr.includes([1, 2]) return false as well, even though this is equal to the last element in the array? And if this method does not work, how else can I find whether my array includes the item [1, 2]?
.includes() method uses sameValueZero equality algorithm to determine whether an element is present in an array or not.
When the two values being compared are not numbers, sameValueZero algorithm uses SameValueNonNumber algorithm. This algorithm consists of 8 steps and the last step is relevant to your code , i.e. when comparison is made between two objects. This step is:
Return true if x and y are the same Object value. Otherwise, return false.
So in case of objects, SameValueZero algorithm returns true only if the two objects are same.
In your code, since the array [1, 2] inside arr array is identically different to [1, 2] that you passed to .includes() method, .includes() method can't find the array inside arr and as a result returns false.
The Array#includes checks by shallow comparison, so in your case the string and numbers are primitives there is only ever a single instance of them so you get true from Array#includes.
But when you check for array, you are passing a new array instance which is not the same instance in the array you are checking so shallow comparison fails.
To check for an array is included in another array first check if it is an array then do a deep comparison between the arrays.
Note that below snippet only works for an array of primitives:
var arr = ['hello', 2, 4, [1, 2]];
const includesArray = (data, arr) => {
return data.some(e => Array.isArray(e) && e.every((o, i) => Object.is(arr[i], o)));
}
console.log(includesArray(arr, [1, 2]));
But if you keep the reference to the array [1, 2] and search with the reference the Array#includes works as in this case the shallow comparison works perfectly (obeying same value zero algorithm):
const child = [1, 2];
const arr = ['hello', 2, 4, child];
console.log(arr.includes(child));
If you don't mind using lodash, this algorithm is accurate - unlike the accepted answer.
import _ from 'lodash';
export const includesArray = (haystack, needle) => {
for (let arr of haystack)
if (_.isEqual(arr, needle)) {
return true
}
return false
}
Related
In this case of concat() function, i don't get why the value of the length property is not concatenated to array. Also when i change the value of length property to some random value, the first two properties are ignored as well when i concatenate them.
1st case:
let arr = [1, 2];
let arrayLike = {
0: "something",
1: "else",
[Symbol.isConcatSpreadable]: true,
length: 2
};
alert( arr.concat(arrayLike) ); // 1,2,something,else
2nd case:
let arr = [1, 2];
let arrayLike = {
0: "something",
1: "else",
[Symbol.isConcatSpreadable]: true,
length: "random",
};
console.log( arr.concat(arrayLike) ); // 1,2
Your arrayLike object is sort of mimicking being an array like arr. So if you're treating it like a normal array, then why would you expect the value of length to be counted as a value in the array?
In your actual array, arr, you would have arr.length == 2. But 2 is not a value in the array, it just tells you how many values are in the array (two values, 1 and 2). This is how JavaScript knows how many values to look for. If you were to set arr.length = 1, suddenly JavaScript would only show one value instead of two. See here:
let arr = [1, 2];
console.log(arr); //[1, 2]
arr.length = 1;
console.log(arr) //[1]
Similarly, your length: 2 property in your arrayLike object is being used to represent an array with 2 elements. If you set it to something that isn't a number, JavaScript no longer knows how many values could be in the "array", so it apparently simply counts it as 0 - an empty array.
I am trying to compare two given parameters of a function. The exact problem is as follows:
You will be provided with an initial array (the first argument in the destroyer function), followed by one or more arguments. Remove all elements from the initial array that are of the same value as these arguments.
Note
You have to use the arguments object.
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3)); // expected output: [1,1]
I am using filter method to iterate over the array but I couldn't compare the args with the elements of the array inside the callback of the filter.
function destroyer(arr, ...args) {
let result = arr.filter(num => {
for (let i = 0; i<=args.length; i++ ){
num !== args[i]
}
});
return result;
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
I can iterate with for loop but I cannot use the output of for loop to do filter iteration.
Any ideas?
Probably an easier way to achieve the goal using .filter() with .includes(). Additionally you can use ...rest so called rest parameters for you function, see form the documentation:
The rest parameter syntax allows us to represent an indefinite number of arguments as an array.
Try as the following:
const destroyer = (arr, ...rest) => {
return arr.filter(num => !rest.includes(num));
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
I hope this helps!
The filter() method creates a new array with all elements that pass the test implemented by the provided function.
Example:
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
const result = words.filter(word => word.length > 6);
console.log(result);
// expected output: Array ["exuberant", "destruction", "present"]
via MDN
Filter iterates over all elements of some array and returns a new array. It puts an element in the new array only if callback (your function invoked as a parameter of filter) return true otherwise it's omitted.
Next it's worth to use rest parameters to achieve two arrays (initial and values to exclude).
The rest parameter syntax allows us to represent an indefinite number of arguments as an array.
function sum(...theArgs) {
return theArgs.reduce((previous, current) => {
return previous + current;
});
}
console.log(sum(1, 2, 3));
// expected output: 6
console.log(sum(1, 2, 3, 4));
// expected output: 10
Solution with explanation:
//Declare your function, first parameter is initial array, the second one is also array created by using rest parameters
function destroyer(initialArray = [], ...toExclude) {
// filter initialArray, if el (single element) is NOT included in "toExclude" it returns true
// and add this particular element to the result array
let result = initialArray.filter(el => toExclude.includes(el) == false);
//return result
return result;
}
Can anyone explain to me how this code works? I looked for reduce and concat functions in Array, I understand these functions but I don't understand how this code works initialy:
var arrays = [[1, 2, 3], [4, 5], [6]];
console.log(arrays.reduce(function(flat, current) {
return flat.concat(current);
}, []));
// → [1, 2, 3, 4, 5, 6]
Well actually it's a wrong use of .reduce(). For this job you don't need no initial array. Just the previous (p) and current (c) hand to hand can do it. Such as;
var arrays = [[1, 2, 3], [4, 5], [6]];
console.log(arrays.reduce((p,c) => p.concat(c)));
Note: Initial is handy when the type of the returned value is different from the array items. However in this case you are processing arrays and returning an array which renders the use of initial redundant.
I've described each step for you.
var arrays = [[1, 2, 3], [4, 5], [6]];
console.log(arrays.reduce(function(flat, current) {
// first loop: flat - [1,2,3], current - [4,5]
// [1,2,3].concat([4,5]) -> [1,2,3,4,5]
//second/last loop: flat - [1,2,3,4,5], current - [6]
// [1,2,3,4,5].concat([6]) -> [1,2,3,4,5,6]
//function stop
return flat.concat(current);
}, []));
You could add a console.log inside the callback we pass to the reduce and think about the output:
var arrays = [[1, 2, 3], [4, 5], [6]];
console.log(arrays.reduce(function(flat, current) {
console.log('flat: '+ flat + 'current: ' + current)
return flat.concat(current);
}, []));
Initially we concat an empty array and the array [1,2,3]. So the result is a new array with elements [1,2,3]. Then we concat this array with the next element of the arrays, the array [4,5]. So the result would be a new array with elements [1,2,3,4,5]. Last we concat this array with the last element of the arrays, the array [6]. Hence the result is the array [1,2,3,4,5,6].
Ir order to understand in details the above you have to read about Array.prototype.reduce().
As it is stated in the above link:
The reduce() method applies a function against an accumulator and each
value of the array (from left-to-right) to reduce it to a single value
Furthermore the syntax is
arr.reduce(callback, [initialValue])
In you case the initialValue is an empty array, [].
So assuming we have the 2D array that you do: [[1, 2, 3], [4, 5], [6]] that is being reduced, the function is split into 2 main components.
array.reduce((accumulator, iterator) => {...}, initialValue);
flat - this is the accumulator of the reduction. It is given the initial value as passed into the second parameter of the reduce function and is used to store the values as the iterator passes through them.
current - this is the iterator that goes through all values within the data set being reduced.
So as you're iterating through the data set, your example is concatenating the accumulation array with the current value, and by the end you have your new array.
Array.reduce expects a callback with following signature:
function(previousElement, currentElement, index, array)
and an optional initial value.
In first iteration, if initialValue is passed, then previousElement will hold this value and currentElement will hold `firstArrayElement.
If not, then previousElement will hold firstArrayElement and currentElement will hold secondArrayElement.
For the following iterations, previousElement will hold value returned by previous iteration and currentElement will hold next value.
So in you example, initially flat holds [].
return flat.concat(current); will return a new merged array. This value will be used as flat for next iteration, and this process is returned. Finally, value returned by last iteration is used as final return value and is printed in console.
I'm trying to set a new state for my react project and I'm stuck on what I'm doing wrong.I want to get the difference of 2 integer arrays
const results = _.difference(items, currSelection);
this.setState({ selected: results });
currSelection is:
[1, 2, 3, 7]
item is:
[1]
when I console.log results, I always get
[]
Reverse the arguments as shown below:
const currSelection = [1, 2, 3, 7];
const items = [1];
const results = _.difference(currSelection, items);
console.log(results); //[2, 3, 7]
_.difference(array, [values])
Creates an array of array values not included in the other given arrays >using SameValueZero for equality comparisons. The order of result values >is determined by the order they occur in the first array.
Arguments
array (Array): The array to inspect.
[values] (...Array): The values to exclude.
Returns
(Array): Returns the new array of filtered values.
I have been doing this course for hours on free code camp, however, I found a solution that I do not understand and I am trying to put comments on each line to record as I achieve and understand it for future references and I already understand some lines but I cannot understand some parts of this code:
function destroyer(arr) {
// let's make the arguments part of the array
var args = Array.prototype.slice.call(arguments); // this would result into [[1, 2, 3, 1, 2, 3], 2, 3]
args.splice(0,1); // now we remove the first argument index on the array so we have 2,3 in this example
// I DO NOT UNDERSTAND THESE CODES BELOW
return arr.filter(function(element) {
return args.indexOf(element) === -1;
});
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
I already check on documentation and I find it hard to understand seems the code in this sample are very different. I would really appreciate your help!
arr in the section of code you don't understand refers to the first argument passed to the destroyer function; in this case, the array [1, 2, 3, 1, 2, 3]
arr.filter is using the Array.filter method to create a "filtered" version of the array with only those values that pass the "test" defined by function(element) { return args.indexOf(element) === -1; }
That function uses Array.indexOf to check if the sliced args array (which you correctly identified as being equal to [2, 3]) contains the given element. Because indexOf returns -1 when the element is not found, checking for that value is equivalent to checking that the specified element is NOT in the array
The result of all of this - and the return value of the function destroy - will be the array [1, 1], representing a filtered version of the array passed to destroy that contains all the values not equal to the other values passed to destroy.
Array.slice is part of the arrays prototype;
prototype methods are only accessable on instances of Classes.
var arr = ['a', 'b', 'c', 'd'];
// [] is JavaScript shorthand for instantiating an Array object.
// this means that you can call:
arr.slice(someArg1);
arry.splice(someArg2);