Array with unique values using array helper functions - javascript

I was playing with ES6 array helper functions reduce() and find(). I'm trying to display array of unique elements. But it is failing in case of value 0. I'm not able to find what's wrong with my code. Please guide.
Here is my code snippet:
var arrayWithDuplicates = [0, 0, 1, 2, 3, 3, 4, 4, 'a', 'a'];
var arrayWithUniqueValues = arrayWithDuplicates
.reduce((previous, item) => {
if(!previous.find(element => element === item)) {
previous.push(item)
}
return previous;
}, []);
console.log('arrayWithUniqueValues', arrayWithUniqueValues)
I'm getting below output:
arrayWithUniqueValues [ 0, 0, 1, 2, 3, 4, 'a' ]
Why I'm getting 0 twice while all other values are unique?

You can achieve the same result by converting your array into a Set and back to an Array.
var arrayWithUniqueValues = [...new Set(arrayWithDuplicates)];
The reason your code doesn't work, by the way, is that Array.prototype.find returns the element it found. When you search for 0, it returns 0 and then !0 is true. So 0 is added even if it is already in the array. You can do instead:
if (previous.indexOf(item) === - 1) {
previous.push(item);
}

The find() method returns the value of the first element in the array that satisfies the provided testing function. Otherwise undefined is returned.
when you got 0,the code becomes :
arrayWithDuplicates.reduce(([0], 0) => {
if(!previous.find(element => element === item)) {
//![0].find(0=>0===0),return 0,so !0 means true
previous.push(item)
//so [0,0]
}
return previous;
});
a better way is
let a=[...new Set([0, 0, 1, 2, 3, 3, 4, 4, 'a', 'a'])];//[0, 1, 2, 3, 4, "a"]

Related

4th argument in reduce

Here is the function:
function chunk(array: number[], size: number): number[][] {
return array.reduce((chunks, curr, _, arr) => {
console.log(arr.length); // -> 10 which is correct
// let len = arr.length; // -> Cannot read properties of undefined (reading 'length')
let len = chunks.length; // this works
if (len === 0 || chunks[len - 1].length === size) chunks.push([curr]);
else chunks[len - 1].push(curr);
return chunks;
}, []);
}
console.log(chunk([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3)); // ->[ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ], [ 10 ] ]
The fourth argument to reduce is the array that we're iterating over. I can log it and I get the correct result (10) see above. But when I try to use it and assign it to a variable I get an error(see above). Could someone please shed some light?
From Mozilla's page, the fourth parameter is the array that is being reduced. You should access the array variable that is already declared, but the fourth parameter works.
For example:
array.reduce((_, __, ___, arr) => {
console.log(arr.length == array.length) // true, this is the source array
});
The reason why you're getting the error is not because of the arr.length property, but rather the way you're accessing chunks.

For two arrays find items that are present in one array only (symmetric difference)

I need to compare two arrays and return a new array with any items only found in one of the two given arrays, but not both. In other words, return the symmetric difference of the two arrays.
My algorithm consists of using the map() method on the first array and compare each of element of that array with the elements of the second array using every(). If this method returns true, the element gets returned on the block level of map (which will eventually add it to the returned array) if not it's discarded.
I'm not sure why my code is not working. This is an example of a wrong output using my code:
function diffArray(arr1, arr2) {
var newArr = arr1
.map(elem1 => {
if (arr2.every(elem2 => elem2 != elem1)) {
return elem1;
}
});
return newArr;
}
console.log(diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]));
This is the wrong output:
[ undefined, undefined, undefined, undefined ]
The expected output is : [4]
Your approach iterates the first array and because of using map along with the check for the value, you get undefined for every element of arr1.
If you take filter and the other array as well, you could get the wanted result.
function diffArray(arr1, arr2) {
return [
...arr1.filter(elem1 => arr2.every(elem2 => elem2 != elem1)),
...arr2.filter(elem1 => arr1.every(elem2 => elem2 != elem1))
];
}
console.log(diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]));
Another approach takes an array of all values of both arrays and filter by checking if the value is not included in both arrays.
function diffArray(arr1, arr2) {
return [...arr1, ...arr2].filter(v => arr1.includes(v) !== arr2.includes(v));
}
console.log(diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]));
Use _.difference(array, [values])
from lodash
or
own solution :
const diffArray = (arrayA, arrayB) => {
const output = []
const setA = new Set(arrayA);
arrayB.forEach((n) =>{
if(!setA.has(n)){
output.push(n)
}
})
const setB = new Set(arrayB);
arrayA.forEach(n =>{
if(!setB.has(n)){
output.push(n)
}
})
return output;
}
console.log(diffArray([1, 2, 3, 5, 6], [1, 2, 3, 4, 5])); //4, 6
This algorithm works even when numbers appear multiple times in both arrays.
It uses a Map created from the items of both arrays. The map contains the item as the key, and the value is the amount of times it's found in the 1st array - the number of times it's found it the 2nd array.
After creating the Map, it's converted to an array of [item, count]. The array is then filtered, and all items with a count of 0 are removed (they exist equally in both arrays), and then we map the array to an array of items.
const getCounts = (arr, init = new Map(), inc = 1) =>
arr.reduce((acc, item) => acc.set(item, (acc.get(item) || 0) + inc), init);
function diffArray(arr1, arr2) {
// create a Map that adds 1 for all items in arr1, and substructs 1 for every item in arr2
const counts = getCounts(arr2, getCounts(arr1), -1);
// convert to an array of pairs [item, count]
return Array.from(counts)
.filter(([, v]) => v) // remove all items with count 0
.map(([k]) => k); // map to the original item
}
console.log(diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]));
console.log(diffArray([1, 2, 3, 3, 5], [1, 2, 3, 4, 5]));
console.log(diffArray([5, 1, 2, 3, 5], [1, 2, 3, 4, 5, 5]));
console.log(diffArray([1, 1, 2, 2, 3, 3, 5], [1, 2, 2, 3, 4, 5]));
Your function returns elements of the first array that are not present in the second array.
Its return is explained by the fact that .map() returns the array of exact same size as your input (arr1), but since all items of arr1 are present in arr2 you don't enter if(-statement body hence undefined is getting returned.
If your intention was to return items that are present in one array only (regardless of the order they're passed in), you may leverage the Map object together with Array.prototype.reduce():
combine arrays-arguments into common array of arrays
loop through those inner arrays with .reduce(), building up the Map, showing how many times each item is seen within combined array
for each item of combined array remove duplicates and increment respective counter
spread resulting Map into .entries() and .filter() those to find out uniques
const arr1 = [1, 2, 3, 5],
arr2 = [1, 2, 3, 4, 5],
getUniques = (...arrays) =>
[...arrays
.reduce((acc, arr) => {
[...new Set(arr)]
.forEach(item =>
acc.set(item, (acc.get(item)||0)+1))
return acc
}, new Map)
.entries()]
.reduce((acc, [item, repetitions]) =>
(repetitions == 1 && acc.push(item), acc), [])
console.log(getUniques(arr1, arr2))
.as-console-wrapper {min-height:100%;}
Above approach is of O(n)-time complexity, as opposed to your initial attempt and the answer you have currently accepted (both having O(n²)-time complexity). As a result it may perform much faster on large arrays (arbitrary number of arrays, as a bonus).

How to remove null values from nested arrays

This code removes all null values from array:
var array = [ 0, 1, null, 2, "", 3, undefined, 3,,,,,, 4,, 4,, 5,, 6,,,, ];
var filtered = array.filter(function (el) {
return el != null;
});
console.log(filtered);
But when I try this on an array with nested arrays that have null values, the nulls are not removed:
var array = [ [ 1, null, 2 ], [ 3, null, 4 ], [ 5, null, 6 ] ];
var filtered = array.filter(function (el) {
return el != null;
});
console.log(filtered);
The expected output is:
[ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ] ]
Instead of the actual output:
[ [ 1, null, 2 ], [ 3, null, 4 ], [ 5, null, 6 ] ]
How can I change my example to filter null values from the nested arrays?
If your array-of-arrays only has one level, then you can just map it like this:
var filtered = array.map(subarray => subarray.filter(el => el != null));
console.log(filtered);
You need to recursively filter for null, like so:
function removeNull(array) {
return array
.filter(item => item !== null)
.map(item => Array.isArray(item) ? removeNull(item) : item);
}
This function takes an array, and recursively removes all instances of null.
First, I took your solution and wrapped it in a function so that it is able to be called.
Then, after the items are filtered, it's as simple as mapping over the remaining items, checking if each one is an array, and then for each one that is, calling removeNull on it.
EDIT: I had a typo in my code originally, but it should work now.
var arraylist = [0, 1, null, 5];
var i = arraylist.length;
var j =0;
var newlist = [];
while(j < i){
if(arraylist[j] != null){
newlist.push(arraylist[j]);
}
j++;
}
console.log(newlist);
https://jsfiddle.net/L4nmtg75/
var filterFn = function(item) {
if (item instanceof Array) {
// do this if you want to remove empty arrays:
var items = item.splice(0).filter(filterFn);
var length = items.length;
Array.prototype.push.apply(item, items);
return length;
// if you want to keep empty arrays do this:
var items = item.splice(0);
Array.prototype.push.apply(item, items.filter(filterFn))
return true;
}
return item != null;
};
array = array.filter(filterFn);
This will also work on more than 2 level, as it's recursive.
You're examples remove undefined values as well as null values, and your expected output reflects that, so I'm going to assume that you mean you want to recursively remove both undefined and null values. Your example uses a loose equality comparison which means that it will match both null and undefined. While this works, it is much better to be explicit about what you're checking for with strict equality comparison using ===.
You're going to need to use recursion:
Recursion
An act of a function calling itself. Recursion is used to solve problems that contain smaller sub-problems.
- https://developer.mozilla.org/en-US/docs/Glossary/Recursion
This also means that you're going to want to use Array#reduce instead of Array#filter. Use a new array as the accumulator.
Then for each element in the input array where the element is not null or undefined:
if the element is an instance of Array, push the result of calling this function on the element onto the accumulator array,
otherwise push the element onto the accumulator array
Return the accumulator array at the end of the reduce callback as the accumulator
const input = [ [ 1, null, 2 ], null,,,,, [ 3, null, 4 ],,,,, [ 5, null, 6 ],,,,, [ 7, [ 8, undefined, 9 ], 10 ] ]
function recursiveValues(input) {
if(!(input instanceof Array)) return null
return input.reduce((output, element) => {
if(element !== null && element !== undefined) {
if(element instanceof Array) {
output.push(recursiveValues(element))
} else {
output.push(element)
}
}
return output
}, [])
}
const output = recursiveValues(input)
console.log(JSON.stringify(output))

Mapping an Array Based on Another Array

I'm working on a project where I need to change the values of one array should the first index of one of its nested arrays be found within the second array. For example:
Array One
[12345, [67890, 1], [09876, 2]]
Array Two
[
[
180547,
'180547 text',
'more text',
...etc
], [
67890,
'67890 text',
'more text',
...etc
],
...etc
]
I need to iterate through each of the nested arrays in Array One and check if the first value in each nested array is present in the any of the arrays in Array Two. From there, I need to copy Array Two's array, and replace the first value of the nested array in Array One.
I know that was probably a little confusing, so here's an example output of the above arrays:
Output
[12345, [[67890, '67890 text', 'more text', ...etc], 1], [09876, 2]]
ES6+ is preferred, but not necessary.
EDIT:
As asked for, so given -
Function jargon aside, here's the best I was able to come up with on my own:
gid.steal()
.then(dataset => {
let o = dataset.forEach(group => {
group.map((person, i) => {
i === 0 ? person : rows.forEach(row => {
row[0] === person[0] && row[row.length - 1] === '-3' ? [row, person[1]] : null
})
})
})
console.log(o)
})
.catch(err => rej(err))
//dataset would be Array One and rows would be Array Two
This returns undefined
I have a feeling I'm either trying to get too complicated/clever with it, or I'm totally out of bounds on this method.
Figured it out. It turns out I was definitely trying to be more complicated than I needed to be. I just needed some simple maps and a filter.
let arr1 = [
['john', 1, 2, 3, 4, '5'],
['jane', 1, 2, 3, 4, '5'],
['jane', 1, 2, 3, 4, '-3'],
['joe', 1, 2, 3, 4, '-3']
]
let arr2 = [
['number', ['jane', 1]]
]
const fx = data => {
return data.map(s => {
return s.map((t, u) => {
return u === 0
? t
: [arr1.filter(v => v[0] === t[0] && v.indexOf('-3') >= 0)[0], t[1]]
})
})
}
console.log(fx(arr2))

Error parsing array through IF Statement

I need to loop through an entire 2D array (OldTable) to check that Column1 has a value of 1 and Col7 is not empty (null). If the above conditions are true then push the current (i) arrays of elements into newTable.
My snippet of JS is as follow...
var newTable = [];
for (var i=1; i<OldTable.length; i++){
if(OldTable[i][0]==1 && OldTable[i][7]!==null){
newTable.push(OldTable[i]);
}
}
Seems like a fairly straight forward thing to do but currently hitting brick wall with this error...
TypeError: Cannot read property "0" from undefined. (line 80, file
"Code"
I have tried to reduce the if statement to just...
if(OldTable[i][0]==1){
...but still the same error.
I'm able to display the array element just fine using...
Browser.msgBox(OldTable[50][0]);
I'm fairly new to JS so could be a simple silly error someone could point out.
UPDATE: In trying to simplying naming, I've actually made it more difficult with conflicting terminology, so have going through and updated the variable names used.
Your code should work if, as noted in the comment by #Massimo, you change your loop from starting at i=1 to i=0, as shown below. Also, just to whet your appetite for more modern tools within JavaScript, I also include an essentially identical solution to the problem using ES6/ES2015.
var myArray = [
[1, 0, 0, 0, 0, 0, 0, 'foo' ], // should pass
[9, 1, 1, 1, 1, 1, 1, 'foo' ], // should fail
[1, 2, 2, 2, 2, 2, 2, 'foo' ], // should pass
[1, 3, 3, 3, 3, 3, 3, null ], // should fail
[0, 4, 4, 4, 4, 4, 4, null ], // should fail
[1, 5, 5, 5, 5, 5, 5, undefined], // should pass
[1, 6, 6, 6, 6, 6, 6, 'foo' ] // should pass
];
function f1(array) {
var newArray = [];
for (var i = 0; i < array.length; i++) {
if (array[i][0] == 1 && array[i][7] !== null) {
newArray.push(array[i]);
}
}
return newArray;
}
const f2 = array => array.filter(e => e[0] === 1 && e[7] !== null);
console.log(f1(myArray));
console.log(f2(myArray));

Categories

Resources