Using Difference from Lodash - javascript

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.

Related

map() method mutating the calling Array

map() can't mutate the calling array, instead it returns a new Array with modified values.
But, the following code mutating the original Array, is there any wrong in my understanding?
const arr = [1, 2, 3, 4, 5];
arr.map((num, index, arr1) => {
return arr1[index] = num * 2;
});
console.log(arr); // [2, 4, 6, 8, 10]
Well, you're mutating the original array by passing its reference into the callback function inside map() (arr1) and then manually accessing the indices. It will create a new array if you just return the value from that function.
const arr = [1, 2, 3, 4, 5];
const arr1 = arr.map((num) => {
return num * 2;
});
console.log(arr); // [1, 2, 3, 4, 5]
console.log(arr1); // [2, 4, 6, 8, 10]
The third argument to the callback function of map is the
original/source array on which the map is called upon
The arr and arr1 are both same i.e both are referencing on the same array, You can see it by using console.log(arr === arr1). So what ever you operation perform on the arr1, it gonna affect the arr.
const arr = [1, 2, 3, 4, 5];
arr.map((num, index, arr1) => {
console.log(arr1 === arr);
return num * 2;
});
You can just return num * 2 from the callback function. map internally creates a new array and return it. So you don't have to assign it as
arr1[index] = num * 2
You can also make it one-liner as:
arr.map((num, index, arr1) => num * 2)
const arr = [1, 2, 3, 4, 5];
const result = arr.map((num, index, arr1) => {
return num * 2;
});
console.log(arr); // [2, 4, 6, 8, 10]
console.log(result); // [2, 4, 6, 8, 10]
Array.map creates a new array populated with the results of calling a provided function on every element in the calling array.
Here its specifed that you must call or execute a function on every element of calling array.
What is the issue with your code?
You are not actually calling a function, you are instead updating the original array. If you are looking to create a new array by multiplying each node of the element with 2, you should do something like below.
Working Example
const arr = [1, 2, 3, 4, 5];
const newArray = arr.map((nodeFromOriginalArray, indexOfCurrentElement, arrayFromMapCalled) => {
return nodeFromOriginalArray * 2;
});
console.log(arr);
console.log(newArray);
Lets debug the paremeters inside the map function.
Here we have provided three arguments.
First argument nodeFromOriginalArray: The current element being processed in the array. This will be each node from your calling array.
Second argument indexOfCurrentElement: The index of the current element being processed in the array. Which means, the index of current element in calling array.
Third argument arrayFromMapCalled: The array map was called upon. This is the array on which the map function is getting executed. Please note, this is the original array. Updating properties inside this array results in updating your calling array. This is what happened in your case.
You should not modify your original array, which is the third parameter. Instead, you should return your node multipled by 2 inside map and assign this to a new array. Updating the third paramater inside the map function will mutate your calling array.
When calling map on an array, you provide a mapper with three arguments, an item in the array, it's index and the array itself (as you've represented in your snippet).
map takes the value returned by the function mapper as the element at the index in a new array returned by the operation.
const arr = [1,2,3,4,5]
const doubled = arr.map(x => x * 2) // [2,4,6,8, 10]
A over simplified implementation of map (without the index and originalArray params) might look like this. Let's assume that instead of being a method on the array instance, it's a function that takes an array and a mapper function.
I would not recommend re-implementing in production code, there's the native implementation as well as several libraries such as lodash and underscore that implement it.
function map(arr, mapper) {
const result = [];
for (const item of arr) {
const resultItem = mapper(item);
result.push(resultItem);
}
return result;
}
function double(x) {
return x * 2;
}
const doubled = map([1,2,3,4,5,6], double); // [2, 4, 6, 8 ,10, 12]

Check if an array includes an array in javascript

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
}

Trying to create copies of an array using spread operator, but some how the array is being mutated

I'm trying to practice with the concept of immutability. I'm using the the spliceTest array as my main reference for creating copies of the array and mutating those. I'm coming to the problem when I declare removeOneItem variable, I somehow can't declare a new spread variable using the same reference of spliceTest.
const removeOneItem = [...spliceTest.splice(0,0), ...spliceTest.splice(1)];
const removeFive = [...spliceTest.splice(0,4), ...spliceTest.splice(5)];
const spreadTest = [...spliceTest];
console.log('removeOneItem:', removeOneItem)
console.log('spreadTest:', spreadTest, spliceTest)
console.log('removeFive:', removeFive)
Results::::::::::::
removeOneItem: [ 2, 3, 4, 5, 6, 7, 8, 9 ]
spreadTest: [] []
removeFive: [ 1 ]
According to MDN:
The splice() method changes the contents of an array by removing or
replacing existing elements and/or adding new elements in place.
This means, that the splice operation changes your array
Immutability of data is a cornerstone of functional programming and in general I'll do what you are trying to do: clone the data and mutate the clone. The following function takes an array and a series of sub-arrays. The sub-arrays consist of [startIndex, quantity]. It clones the original array by the spread operator and splices the clone according to the second parameter (...cutDeep). It will return an object with the original array and the cloned array. If you wrap everything in a function then your scope protects each return. Note on subsequent turns The second clone (secondResult.dissected) is spliced once more and the last log proves the original array is never mutated.
Demo
const data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f'];
const dissect = (array, ...cutDeep) => {
let clone = [...array];
for (let [cut, deep] of cutDeep) {
clone.splice(cut, deep);
}
return {
original: array,
dissected: clone
};
}
const firstResult = dissect(data, [2, 3], [5, 2], [9, 1]);
const secondResult = dissect(data, [3, 2], [10, 1]);
console.log(JSON.stringify(firstResult));
console.log(JSON.stringify(secondResult));
console.log(JSON.stringify(dissect(secondResult.dissected, [0, 2], [5, 1])));
console.log(JSON.stringify(data));
The problem is that you use splice when you most likely want to use slice.
splice is used for mutating an array, while slice is used to select a sub-array.
const sliceTest = [1, 2, 3, 4, 5, 6, 7, 8, 9];
// select a sub-array starting from index 1 (dropping 0)
const removeOneItem = sliceTest.slice(1);
// select a sub-array starting from index 5 (dropping 0, 1, 2, 3, and 4)
const removeFive = sliceTest.slice(5);
// spread the full array into a new one
const spreadTest = [...sliceTest];
// array log helpers (leave these out in your code)
const toString = array => "[" + array.join(",") + "]";
const log = (name, ...arrays) => console.log(name, ...arrays.map(toString));
log('removeOneItem:', removeOneItem)
log('spreadTest:', spreadTest, sliceTest)
log('removeFive:', removeFive)
slice already creates a shallow copy of the array, so [...arr.slice(i)] is not needed.

JS - For Loops Pushing Array

I have an initial array,
I've been trying to change values (orders) by using pop, splice methods inside a for loop and finally I push this array to the container array.
However every time initial array is values are pushed. When I wrote console.log(initial) before push method, I can see initial array has been changed but it is not pushed to the container.
I also tried to slow down the process by using settimeout for push method but this didnt work. It is not slowing down. I guess this code is invoked immediately
I would like to learn what is going on here ? Why I have this kind of problem and what is the solution to get rid of that.
function trial(){
let schedulePattern = [];
let initial = [1,3,4,2];
for(let i = 0; i < 3; i++){
let temp = initial.pop();
initial.splice(1,0,temp);
console.log(initial);
schedulePattern.push(initial);
}
return schedulePattern;
}
**Console.log**
(4) [1, 2, 3, 4]
(4) [1, 4, 2, 3]
(4) [1, 3, 4, 2]
(3) [Array(4), Array(4), Array(4)]
0 : (4) [1, 3, 4, 2]
1 : (4) [1, 3, 4, 2]
2 : (4) [1, 3, 4, 2]
length : 3
When you push initial into schedulePattern, it's going to be a bunch of references to the same Array object. You can push a copy of the array instead if you want to preserve its current contents:
schedulePattern.push(initial.slice(0));
Good answer on reference types versus value types here: https://stackoverflow.com/a/13266769/119549
When you push the array to schedulepattern, you are passing a reference to it.
you have to "clone" the array.
use the slice function.
function trial(){
let schedulePattern = [];
let initial = [1,3,4,2];
for(let i = 0; i < 3; i++){
let temp = initial.pop();
initial.splice(1,0,temp);
console.log(initial);
schedulePattern.push(initial.slice());
}
return schedulePattern;
}
​
You have to know that arrays are mutable objects. What does it mean? It means what is happening to you, you are copying the reference of the object and modifying it.
const array = [1,2,3]
const copy = array;
copy.push(4);
console.log(array); // [1, 2, 3, 4]
console.log(copy); // [1, 2, 3, 4]
There are a lot of methods in Javascript which provide you the way you are looking for. In other words, create a new array copy to work properly without modify the root.
const array = [1,2,3]
const copy = Array.from(array);
copy.push(4);
console.log(array); // [1, 2, 3]
console.log(copy); // [1, 2, 3, 4]
I encourage you to take a look at Array methods to increase your knowledge to take the best decision about using the different options you have.

Javascript array higher-order functions

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.

Categories

Resources