How to match duplicate values in nested arrays in JS - javascript

I have an array which contains nested arrays that are dynamically created, it looks like this:
[['1', '2'],['1','3', '4'],['1', '3']]
I am trying to implement AND logic by getting only duplicate values from these arrays. My expected output here would be ['1'] since all nested arrays must contain the same value.
// [['1', '2'],['1','3', '4'],['1', '3']]
const arrays = [...new Set(response)].filter(newSet => newSet.length > 0);
const builder = []; // array of all the id's no longer needed
// [[],[],[]]
arrays.forEach(arr => {
// []
arr.forEach(value => {
// [[], [], []]
const found = arrays.filter(a => a.find(v => v === value));
if (found.length === 0)
builder.push(value);
});
});
console.log(builder); // empty []
This gives me an empty array because of the filter(). How could I return an array of values that all (3 in this case but could be more) arrays contain?
Expected output with the above input would be ["1"]. Any help appreciated.

from what I understand you need the common elements from all array
let response1 = [['1', '2'],['1','3', '4'],['1', '3']]
let response2 = [['1', '2', '3'],['1','3', '4'],['1', '3']]
const getCommon = res => [...new Set(res.flat())].filter(a => res.every(c => c.includes(a)));
console.log(getCommon(response1))
console.log(getCommon(response2))
UPDATE
Apparently the set transformation is unnecessary since anyway it has to give the elements common to every array so filtering from res[0] should do
let response1 = [['1', '2'],['1','3', '4'],['1', '3']]
let response2 = [['1', '2', '3'],['1','3', '4'],['1', '3']]
const getCommon = res => res[0].filter(a => res.every(c => c.includes(a)));
console.log(getCommon(response1))
console.log(getCommon(response2))

You can make a count object that has the frequency of each number in it, and just check if the frequency of a number is equal to the length of the original array.
const getIntersectVals = (arrayOfVals)=>{
const freqs = {};
for(let arr of arrayOfVals){
for(let val of arr){
if(freqs[val]) freqs[val]++;
else freqs[val] = 1;
}
}
const uniqueVals = Object.keys(freqs);
const correctVals = uniqueVals.filter(elem=>{
return freqs[elem] === arrayOfVals.length;
})
return correctVals;
}
const arrayOfVals = [['1', '2'],['1','3', '4'],['1', '3']];
console.log(getIntersectVals(arrayOfVals))

Lodash intesection if you don't mind
const arrayOfVals = [['1', '2'],['1','3', '4'],['1', '3']];
const result = _.intersection(...arrayOfVals);
console.log(result);
.as-console-wrapper{min-height: 100%!important; top: 0}
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.21/lodash.min.js"></script>

Related

How to add an element to an array inside an array in JS ES6?

For example, I know I can use spread operator to add an element to an array in ES6.
const arr = ["a","b"]
const newArr = [...arr, "c"]
that I get
["a","b","c"]
But how to implement this in an nested array, like
const arr = [["a","b"], ["d"]]
I want to get
newArr1 = [["a","b","c"], ["d"]]
and
newArr2 = [["a","b"],["d","c"]]
Perhaps we can use
[[...arr[0], 'c'], [...arr[1]]]
but what if
const arr = [["a","b"], ["d"],["e"]]
and I still want
[["a","b","c"], ["d"],["e"]]
is there a common way?
You can simply use the array index or use findIndex and find methods, in case you want to remove not based on the index
const arr = [[a,b], [d]]
arr[1].push(c)
//arr = [[a,b], [c,d]]
const arr = [['a', 'b'], ['d']]
const array = [...arr]
const arrayIndex = array.findIndex(item => item.find(i => i === 'd'))
if (arrayIndex !== -1) {
const oldArray = array.find(item => item.find(i => i === 'd'))
const newArray = [...oldArray, 'c']
array[arrayIndex] = newArray
}
console.log(array);

Filter by index of nested array in javascript

I have an array in javascript which contains a number of nested arrays
let data = [
["Apple","Fruit","Red"],
["Pear","Fruit","Green"],
["Orange","Fruit","Orange"],
["Carrot","Vegetable","Orange"],
["Pea","Vegetable","Green"],
["Pumpkin","Vegetable","Orange"]
]
From this array, I wish to create two new arrays. Arr1 is the unique food types (index2) and Arr2is the unique colors (Index 3).
My new arrays should be:
Arr1 = ["Fruit","Vegetable"]
Arr2 = ["Red","Green","Orange"]
I have managed to achieve this by using for each, where I've pushed every second object to an array. I then filter this new array for unique values. This is the code I'm using to do this:
var Foods = []
var Colors = []
for (var key in data) {
Foods.push(data[key][1]);
}
for (var key in data) {
Colors.push(data[key][2]);
}
let Arr1 = [...new Set(Foods)]
let Arr2 = [...new Set(Colors)]
console.log(Arr1)
console.log(Arr2)
Although this works well, as a javascript beginner, I thought there may be a more elegant way to achieve.
For example is it not possible to filter all the unique values of data with an index of [2]?
Your code is fine as it is, the only improvement would be to replace loops with map:
let foods = [...new Set(data.map(d => d[1]))]
let colors = [...new Set(data.map(d => d[2]))]
There's absolutely no need to overcomplicate matters with reduce, destructuring and so on.
If you want it to look slightly more elegant, you can define two utility functions
const item = i => a => a[i]
const uniq = a => [...new Set(a)]
and then
let foods = uniq(data.map(item(1)))
let colors = uniq(data.map(item(2)))
Another option:
const uniq = a => [...new Set(a)]
const zip = (...args) => args[0].map((_, i) => args.map(a => a[i]))
let [_, foods, colors] = zip(...data).map(uniq)
Reduce the array to two Sets, and then destructure the Sets, and spread each into the respective array:
const data = [["Apple","Fruit","Red"],["Pear","Fruit","Green"],["Orange","Fruit","Orange"],["Carrot","Vegetable","Orange"],["Pea","Vegetable","Green"],["Pumpkin","Vegetable","Orange"]]
const [s1, s2] = data.reduce((acc, arr) => {
acc.forEach((s, i) => s.add(arr[i + 1]))
return acc
}, [new Set(), new Set()])
const Arr1 = [...s1]
const Arr2 = [...s2]
console.log({ Arr1, Arr2 })
You could take a single loop and get the unique values for all items of the nested arrays.
const
getUnique = array => array
.reduce((r, a) => a.map((v, i) => (r[i] || new Set).add(v)), [])
.map(s => [...s]),
data = [["Apple", "Fruit", "Red"], ["Pear", "Fruit", "Green"], ["Orange", "Fruit", "Orange"], ["Carrot", "Vegetable", "Orange"], ["Pea", "Vegetable", "Green"], ["Pumpkin", "Vegetable", "Orange"]],
[, types, colors] = getUnique(data);
console.log(types);
console.log(colors);
Create an object with foods and colors as keys and loop through inserting the appropriate element index each iteration
let data = [
["Apple","Fruit","Red"],
["Pear","Fruit","Green"],
["Orange","Fruit","Orange"],
["Carrot","Vegetable","Orange"],
["Pea","Vegetable","Green"],
["Pumpkin","Vegetable","Orange"]
]
const uniques = {
foods:new Set(),
colors: new Set()
}
data.forEach(a => uniques.foods.add(a[1]) && uniques.colors.add(a[2]) )
// just for demo logs
Object.entries(uniques).forEach(([k,set])=> console.log(k,':', ...set))

Flatten An array element wise in JavaScript

I have two Arrays which look like this:
array1: [["abc","def","ghi"],["jkl","mno","pqr"]],
array2: [[1,2,3,4,5],[6,7,8,9,10]]
I want to operate a Flattening operation which gives me result like this: Flatten(array1,array2):
result: [["abc","def","ghi",1,2,3,4,5],["jkl","mno","pqr",6,7,8,9,10]]
Any suggestions on the same?
Edit 1: Both the Arrays always have the same length.
You can use map() on one of them and concat() it with corresponding element of other array
Note: I am considering length of both the arrays will be equal
const arr1 = [["abc","def","ghi"],["jkl","mno","pqr"]];
const arr2 = [[1,2,3,4,5],[6,7,8,9,10]];
const flattern = (a1, a2) => a1.map((x, i) => x.concat(a2[i]))
console.log(flattern(arr1, arr2))
If lengths of arrays are not same then you will have to first find the larger array and then map over it.
const arr1 = [["abc","def","ghi"],["jkl","mno","pqr"], ['a','b','c']];
const arr2 = [[1,2,3,4,5],[6,7,8,9,10]];
const flattern = (a1, a2) =>{
if(a1.length === a2.length){
return a1.map((x, i) => x.concat(a2[i]))
}
else if(a1.length > a2.length){
return a1.map((x, i) => x.concat(a2[i] || []))
}
else{
return a2.map((x, i) => x.concat(a1[i] || []))
}
}
console.log(flattern(arr1, arr2))
Since the length of the array is same, you could use map() over one array and concat the other.
const array1 = [["abc","def","ghi"],["jkl","mno","pqr"]];
const array2 = [[1,2,3,4,5],[6,7,8,9,10]];
let result = array1.map((a, i) => a.concat(array2[i]));
console.log(result);
You can use [...].flat(), see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat

Js Filter nested array

Here is the code
let nestedArray = [[1,2], [3,4], [5,6,7], [8,9,10]]
let arr = [2,3,6,7]
I want nestedArray filter all element equal arr element, then return
result = [[2],[3],[6,7]]
Here is my solution, it won't work.
arr.forEach(id => {
let result = nestedArray.filter(nest => {
return nest.filter(item => item === id)
})
Each time it only output a single match
let nestedArray = [[1,2], [3,4], [5,6,7], [8,9,10]]
let arr = [2,3,6,7]
let result = nestedArray.map(array =>
array.filter(item => arr.includes(item))
).filter((a) => a.length);
console.log(result)
nestedArray.map(
na=>na.filter(
num=>arr.includes(num)
)
).filter(
na=>na.length!==0
)

How to get "unfiltered" array items?

Let's say I have an array which I filter by calling myItems.filter(filterFunction1) and get some items from it.
Then I want to run another filtering function filterFunction2 against the remaining items which were not selected by filterFunction1.
Is that possible to get the remaining items that were left out after calling a filtering function?
You'd have to rerun the filter with an inverted predicate, which seems wasteful. You should reduce the items instead and bin them into one of two bins:
const result = arr.reduce((res, item) => {
res[predicate(item) ? 'a' : 'b'].push(item);
return res;
}, { a: [], b: [] });
predicate here is the callback you'd give to filter.
Unfortunately, there is no one-step solution based on filter. Still the solution is a simple one-liner:
Here's an example
const arr = [ 1,2,3,4,5,6,7,8 ];
const filtered = arr.filter(x=>!!(x%2))
const remaining = arr.filter(x=>!filtered.includes(x))
console.log(filtered, remaining);
You could map an array of flags and then filter by the flags values.
const cond = v => !(v % 2);
var array = [1, 2, 3, 4, 5],
flags = array.map(cond),
result1 = array.filter((_, i) => flags[i]),
result2 = array.filter((_, i) => !flags[i]);
console.log(result1);
console.log(result2);
You can achieve that using Array.reduce.
const myItems = [...];
const { result1, result2 } = myItems.reduce(
(result, item) => {
if (filterFunc1(item)) {
result.result1.push(item);
} else if (filterFunc2(item)) {
result.result2.push(item);
}
return result;
},
{ result1: [], result2: [] },
);
If you don't want to use reduce, you may want to iterate the array once and acquire the filtered and unfiltered items in a single shot, using a plain efficient for..of loop:
function filterAndDiscriminate(arr, filterCallback) {
const res = [[],[]];
for (var item of arr) {
res[~~filterCallback(item)].push(item);
}
return res;
}
const [unfiltered, filtered] = filterAndDiscriminate([1,2,3,4,5], i => i <= 3);
console.log(filtered, unfiltered);
There's a way more simple and readable way to do this:
const array1 = []
const array2 = []
itemsToFilter.forEach(item => item.condition === met ? array1.push(challenge) : array2.push(challenge))

Categories