How use reduce() to concat an array and delete the items duplicated - javascript

I have a problem using the reduce function in JavaScript. My goal is to take an array of arrays and, using reduce, then concat all the values and delete the duplicates:
I have an array like this:
firstArray = [[1, 2],[2, 3, 4],[4, 5, 6]]
I need an array like this after apply reduce():
resultArray = [1, 2, 3, 4, 5, 6]
const arrayElements = [[1,2],[2,3,4],[5,6]];
const newArray = arrayElements.reduce((total, value) => {
return total.concat(value);
}, []);
This is the fragment that I have, this only concat all the values, this is to say, [1, 2, 2, 3, 4, 5, 6]

var num = [[1,2],[2,3,4],[5,6]];
var unique_numbers = [].concat(...num);
unique_numbers = Array.from(new Set(unique_numbers ));

You can use Set and flat
const array = [[1,2],[2,3,4],[5,6]];
const newArray = [...new Set(array.flat(Infinity))]
console.log(newArray)

Your reduce is correct, just add the data to a set, and convert the set to an array using spread syntax ...
const arrayElements = [[1,2],[2,3,4],[5,6]];
const newArray = [...new Set(arrayElements.reduce((total, value) => total.concat(value), []))];
console.log(newArray)

You can do this using Set:
const newArray = arrayElements.reduce((accum, arr) => [...accum, ..arr], [])
const arrNoDupes = [...new Set(newArray)]
or using filter:
const newArray = arrayElements.reduce((accum, arr) => [
...accum
...arr.filter(value => !accum.includes(value))
], [])
These are just two ways you can do what you want to achieve.

Here is the old fashioned ES5 way in case browser support is a concern.
var arrayElements = [[1,2],[2,3,4],[5,6]];
var newArray = arrayElements.reduce(function(total, value){
return total.concat(value).filter(function (x, i, a) {
return a.indexOf(x) == i;
});
}, []);
console.log(newArray)

Related

How to get a reduced array? My array is not getting returned

I am trying to get a reduced array.
My output is supposed to be [1,5,4] but it gives me an empty array.
let arr=[1,[2,3],4]
let newarr=[]
let myarr=()=>{
for(i=0;i<3;i++){
array=arr[i].reduce
newarr.push(array)
return newarr
}
}
You need to pass a function to your array.reduce,also you have to actually call your function,like so ( console.log calls it ):
const arr = [1, [2, 3], 4];
const myArr = (arr) => {
let newArray = [];
arr.forEach((arrayItem) => {
//we iterate every item in given array
if (Array.isArray(arrayItem)) {
//we check if item is actually an array to reduce
newArray.push(
arrayItem.reduce(function (previous, current) {
return previous + current;
})
); //if its an array then reduce it down to a single value using a reducer function and push it in new array
} else {
newArray.push(arrayItem); //otherwise just push it in new array as is
}
});
return newArray;
};
console.log( myArr(arr) );
there are always shorter and prettier ways to do the same,above solution is as readable as possible to understand it,an one-liner would be :
const arr = [1, [2, 3], 4]
const newArr = arr.map(i=>Array.isArray(i)?i.reduce((a,b)=>a+b):i)
console.log(newArr)
Array#reduce is an array method and it needs a function to be passed to it. Also you have defined a function but you do not invoke it.
Try the following:
let arr = [1, [2, 3], 4];
let newarr = [];
((array) => {
for(let i=0; i < array.length; i++) {
const el = array[i];
const topush = Array.isArray(el) ? el.reduce((total, curr) => total + curr, 0) : el;
newarr.push(topush)
}
})( arr );
console.log( newarr );
Alternatively, you can use Array#map and Array#reduce methods as follows. In both cases the trick is to identify the array so as to apply reduce to it:
const arr = [1, [2, 3], 4, [4, 5, 7]];
const newarr = arr.map(el =>
Array.isArray(el) ? el.reduce((sum,cur) => sum + cur,0) : el
);
console.log( newarr );

Find elements in an array not contained in another array of objects

I have an array arr1 = [1,2,3,4,5]
There is another array of objects arr2 = [{'id':2, 'name':'A'},{'id':4, 'name':'B'}]
I am looking for find elements in arr1 which are not in arr2. The expected output is [1,3,5]
I tried the following but it doesn't work.
const arr = arr1.filter(i => arr2.includes(i.id));
Can you please help?
A solution with O(arr2.length) + O(arr1.length) complexity in Vanilla JS
var arr1= [1,2,3,4,5];
var arr2 = [{'id':2, 'name':'A'},{'id':4, 'name':'B'}];
var tmp = arr2.reduce(function (acc, obj) {
acc[obj['id']] = true;
return acc;
}, {});
var result = arr1.filter(function(nr) {
return !tmp.hasOwnProperty(nr);
})
arr2 is an array of objects, so arr2.includes(i.id) doesn't work because i (an item from arr1) is a number, which doesn't have an id property, and because arr2 is an array of objects.
Turn arr2's ids into a Set first, then check whether the set contains the item being iterated over:
const arr1 = [1,2,3,4,5];
const arr2 = [{'id':2, 'name':'A'},{'id':4, 'name':'B'}];
const ids = new Set(arr2.map(({ id }) => id));
const filtered = arr1.filter(num => !ids.has(num));
console.log(filtered);
You can try with Array.prototype.some():
The some() method tests whether at least one element in the array passes the test implemented by the provided function. It returns a Boolean value.
const arr1 = [1,2,3,4,5]
const arr2 = [{'id':2, 'name':'A'},{'id':4, 'name':'B'}]
const arr = arr1.filter(i => !arr2.some(j => j.id == i));
console.log(arr);
We can use the filter method like below to check the condition required
var arr1 = [1, 2, 3, 4, 5]
var arr2 = [{ 'id': 2, 'name': 'A' }, { 'id': 4, 'name': 'B' }]
var ids = [];
arr2.forEach(element => {
ids.push(element['id'])
});
var result = arr1.filter(s => ids.indexOf(s) < 0)
console.log(result)
let arr1= [1,2,3,4,5];
let arr2 = [{'id':2, 'name':'A'},{'id':4, 'name':'B'}]
let arr2Ids=arr2.map(item=>item.id);
let result=arr1.filter(n => !arr2Ids.includes(n));
You can use find on arr2 instead of includes since arr2 is composed of object
const arr = arr1.filter(i => !arr2.find(e => e.id===i));

How to group by two array by index in javascript with es6 or lodash?

We have same two arrays to groupby theme by index.
Two arrays with same length and different value like blow.
How to groupby two array with their index by ES6 reduce or lodash?
array1 = [1,2,3,4] OR [{a:1},{b:2},{c:3},{d:4}]
array2 = [5,6,7,8] OR [{e:5},{f:6},{g:7},{h:8}]
finalArray = [[1,5],[2,6],[3,7],[4,8]]
I'm trying with different ways like group by with reduce in es6 or lodash concat but i can't find best solution for my problems.
Try this:
let array1 = [1, 2, 3, 4];
let array2 = [5, 6, 7, 8];
let res = array1.map((value, index) => {
return [value, array2[index]]
})
console.log(res);
If it is array of objects
let array1 = [{a:1},{b:2},{c:3},{d:4}];
let array2 = [{e:5},{f:6},{g:7},{h:8}];
let res = array1.map((value, index) => {
return [Object.values(value)[0],Object.values(array2[index])[0]]
})
console.log(res)
Use lodashes zip function
// _ is lodash
const array1 = [1,2,3,4]
const array2 = [5,6,7,8]
console.log(_.zip(array1, array2))
result
[ [ 1, 5 ], [ 2, 6 ], [ 3, 7 ], [ 4, 8 ] ]
If you are working with the array of objects. Get just the values using Object.values and grab the 0th element.
const array3 = [{a:1},{b:2},{c:3},{d:4}];
const array4 = [{e:5},{f:6},{g:7},{h:8}];
function firstval(ob){
return Object.values(ob)[0]
}
console.log(_.zip(array3.map(firstval), array4.map(firstval)))
You can also write your own zip. This is a limited version. That handles only 2 elements, doesn't accept or return generators etc.
It could easily be extended to take a spread operator and therefore any number of arguments. You don't seem to need that level of flexibility though.
function zip(a, b) {
const num = Math.min(a.length, b.length);
const result = [];
for(i = 0; i < num; i++) result.push([a[i], b[i]]);
return result;
}
Following code works under these assumptions:
all input arrays have same length
if array element is object, it has only one property
function getValue(element) {
if (typeof element === 'object') {
return Object.values(element).pop()
} else {
return element
}
}
function zipArrays(arr1, arr2) {
return arr1.reduce((acc, elm1, index) => {
const elm2 = arr2[index]
const elm = [getValue(elm1), getValue(elm2)]
acc.push(elm)
return acc
}, [])
}
// usage:
const array1 = [1,2,3,4] // OR [{a:1},{b:2},{c:3},{d:4}]
const array2 = [5,6,7,8] // OR [{e:5},{f:6},{g:7},{h:8}]
const finalArray = zipArrays(array1, array2)

Can we use one Array.filter() method over several arrays?

I am currently filtering three arrays to return only the iterations that match the paratemer, something like:
filterMyArrays(parameter) {
this.array1 = this.array1.filter(e => (e.item === parameter));
this.array2 = this.array1.filter(e => (e.item === parameter));
this.array3 = this.array1.filter(e => (e.item === parameter));
}
where all three arrays are based off the same original data return.
The code above (well, pseudocode, but you get the idea) is working as expected, but I was wondering if there is a method to create one single array.filter() instance that I can then invoke over several arrays, so my code becomes a bit cleaner and less repetitive. Something like, perhaps:
const myArrays = [array1, array2, array3]
myArrays.filter(e => (e.item === parameter));
or something like
let filterMySelection(desiredArray) = desiredArray.filter(e => (e.item === parameter));
array1 = filterMySelection(array1);
I have fiddled around with both attempts, but none of them worked.
you can use map along with filter as follows:
const combined_arrays = [array_1, array_2, array_3];
let result = combined_arrays.map(function(arr) {
return [arr.filter(e => (e.item == parameter))];
});
const myArrays = [array1, array2, array3];
const filteredArrays = myArrays.map(
array => array.filter(e => e.item === parameter)
);
// OR
let filterMySelection = (desiredArray) => desiredArray.filter(
e => e.item === parameter
);
const array1 = filterMySelection(array1);
var array = [1, 3, 6, 8, 11];
var array2 = [4, 2, 5, 6, 15];
var array3 = [7, 4, 2, 6, 14];
var a = []
a.push(array)
a.push(array2)
a.push(array3)
// OR
// a.push([1, 3, 6, 8, 11])
// a.push([4, 2, 5, 6, 15])
// a.push([7, 4, 2, 6, 14])
a.forEach((array, index) => a[index] = array.filter(function(number) { return number > 7; }))
var lucky = array.filter(function(number) {
return number > 7;
});
console.log(a);
You can try something like this:
Idea:
Save reference of all necessary array in another array.
Loop on it using .map and return filtered list.
Inside each iteration, filter current array and return result.
Override necessary on this with filtered values
var arrs = [ this.array1, this.array2, this.array3 ];
var [ array1, array2, array3 ] = arrs.map((arr) => arr.filter((e) => e.item === parameter ));
Object.assign(this, { array1, array2, array3 });
You can also save filter function in a variable and use it in all filters:
filterMyArrays(parameter) {
const filterFn = (e) => (e.item === parameter)
this.array1 = this.array1.filter(filterFn);
this.array2 = this.array1.filter(filterFn);
this.array3 = this.array1.filter(filterFn);
}
You can use a mixture of map and filter.
const myArrays = [array1, array2, array3];
let newArray = myArrays.map(k => k.filter(e => e.item === parameter));

Using es6 spread to concat multiple arrays

We all know you can do:
let arr1 = [1,2,3];
let arr2 = [3,4,5];
let arr3 = [...arr1, ...arr2]; // [1,2,3,3,4,5]
But how do you make this dynamic to concat N arrays?
One option is to use reduce:
let arrs = [[1, 2], [3, 4], [5, 6]];
arrs.reduce((a, b) => [...a, ...b], []);
Of course, this is a slow solution (quadratic time). Alternatively, if you can use Lodash, _.flatten does exactly what you want, and does it more efficiently (linear time).
EDIT
Or, adapted from Xotic750's comment below,
[].concat(...arrs);
Which should be efficient (linear time).
Another option could be:
const nArrays = [
[1, 2, 3, 4, 5],
[6, 7, 8, 9],
[10, 11]
];
const flattened = [].concat(...nArrays);
console.log(flattened)
let fruits = ["apples", "bananas", "pears"];
let vegetables = ["corn", "potatoes", "carrots"];
let produce = [...fruits, ...vegetables];
console.log(produce);
Best option is to use FlatMap, which helps us to conact multiple arrays into one single array.
Example:
let arrs = [[1, 2], [3, 4], [5, 6]];
arrs.flatMap(a => a);
result will be
> (6) [1, 2, 3, 4, 5, 6]
Happy Coding...
Following solution works for me (spread operator in ES6):
let array = ['my','solution','works'];
let newArray = [];
let newArray2 = [];
newArray.push(...array); //adding to same array
newArray2.push([...array]); //adding as child/leaf/sub-array
console.log(newArray);
console.log(newArray2);
You can't do that with spread syntax alone, as spread syntax requires you to know how many arrays you are concatenating in advance. However, you could write the following function:
function concatN(...arguments) {
let accumulator = [];
for(let arg = 0; arg < arguments.length; arg = arg + 1) {
accumulator = [...accumulator, ...arguments[arg]];
}
return accumulator;
}
It probably won't be very efficient, though (repeated use of spread syntax is O(n²)). Using Array.prototype.concatwould be better. You can just do:
[].concat(all, of, your, arrays);
You can use spread element within for..of loop to concatenate array values to a single array
let arr1 = [1,2,3];
let arr2 = [3,4,5];
let arr3 = [];
for (let arr of [arr1, arr2 /* , arrN */]) arr3.push(...arr);
console.log(arr3);
You could use a recursive function and Array.prototype.concat
const concatN = (x,...xs) =>
x === undefined ? [] : x.concat(concatN(...xs))
console.log(concatN([1,2,3], [4,5,6], [7,8,9]))
// [1,2,3,4,5,6,7,8,9]
You can do the same thing using reduce and Array.prototype.concat. This is similar to the accepted answer but doesn't senselessly use spread syntax where x.concat(y) is perfectly acceptable (and likely heaps faster) in this case
const concatN = (...xs) =>
xs.reduce((x,y) => x.concat(y), [])
console.log(concatN([1,2,3], [4,5,6], [7,8,9]))
// [1,2,3,4,5,6,7,8,9]
We can resolve using es6 following way
function mergeTwo(arr1, arr2) {
let result = [...arr1, ...arr2];
return result.sort((a,b) => a-b);
}
let arr1 = [1,2,3];
let arr2 = [3,4,5];
let arrs = [arr1, arr2].flat(); // [1,2,3,3,4,5]
console.log(arrs);

Categories

Resources