Remove multiple elements from nested arrays - javascript

Assume there is a nested array like this:
[
[ 'one', 'third ],
[ 'one', 'second', 'fourth' ],
[ 'one', 'third' ],
]
I need to make the values unique by order priority: If an element is existing in the first array, it should be removed from the second and third. An element of the second array should not exist in the third.
So the result should be:
[
[ 'one', 'third ],
[ 'second', 'fourth' ],
[],
]
I would iterate over each array and each element, but this removes an element only from the next array (which is missing the last array or errors if the loop is at the last array) and it feels very hacky...
for (let i = 0; i < array.length; i++) {
const element = array[i];
for (let j = 0; j < element.length; j++) {
const string = element[j];
const index = array[i + 1].indexOf(string)
if (index !== -1) {
array[i + 1].splice(index, 1)
}
}
}

You could take a Set and filter the values with a lookup and adding the value, if not seen.
const
values = new Set,
data = [['one', 'third'], ['one', 'second', 'fourth'], ['one', 'third']],
result = data.map(a => a.filter(v => !values.has(v) && values.add(v)));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

It doesn't get much simpler than what you have done. Here is an alternative approach using a nested .forEach() and a hash u to keep track of already encountered array elements:
const arr=[
[ 'one', 'third' ],
[ 'one', 'second', 'fourth' ],
[ 'one', 'third' ],
];
const res=[], u={};
arr.forEach(r=>{
res.push([]);
r.forEach((c,i)=>{
u[c] || (u[c]=res[res.length-1].push(c))
})});
console.log(res);

This can be done via a two step process:
Use Set on each sub-array to remove any duplicate elements
Use .filter() and .some() methods to iterate through each element of each sub-array and leave out (filter) any that are in any lower-index sub-array. The first sub-array - index 0 - is returned whole as there are no lower-index sub-arrays to compare with.
const data = [[ 'one', 'third'],[ 'one','second','fourth'], ['one', 'third']];
const result = data.map(arr => [...new Set(arr)]).map(
(arr,i,a) => i === 0 ? arr : arr.filter(
v => !a.slice(0,i).some(x => x.includes(v))
)
);
console.log( result );

An immutable approach, using reduce() and flat():
const data = [[ 'one', 'third' ], [ 'one', 'second', 'fourth' ],[ 'one', 'third' ]];
const result = data.reduce((acc, val) => [
...acc,
val.filter(item => !acc.flat().includes(item))
], []);
console.log(result);

Related

How do I convert an array object into a two-dimensional array

Here is a Array list.
const list = [
{
id: 5844,
option: 'fruit'
children: ['apple', 'banana', 'pear']
},
{
id: 5845,
option: 'vegetables'
children: ['tomato', 'potato', 'spinach']
}
]
I want to get a new array like this
apple of fruit's children is index 0
tomato of vegetables's children is index = 0
so they are match
[['apple', 'tomato'], ['banana', 'potato'], ['pear', 'spinach']]
With this solution it doesn't matter how many objects are in the array. You can map over the children in the first object and use it's length to return a flatMap of the children elements.
const list=[{id:5844,option:"fruit",children:["apple","banana","pear"]},{id:5845,option:"vegetables",children:["tomato","potato","spinach"]},{id:5846,option:"buildings",children:["church","warehouse","skyscraper"]}];
function getNewData(list) {
// `map` over the children in the first object
// using its index to return a new flattened array
// of all array object children
return list[0].children.map((_, i) => {
return list.flatMap(obj => obj.children[i]);
});
}
console.log(getNewData(list));
I think we can try this piece of code
const list = [
{
id: 5844,
option: 'fruit',
children: ['apple', 'banana', 'pear']
},
{
id: 5845,
option: 'vegetables',
children: ['tomato', 'potato', 'spinach']
}
]
var ans = []
list.forEach(item => {
item.children.forEach((child, index) => {
if (!ans[index]) {
ans[index] = []
ans[index].push(child)
} else {
ans[index].push(child)
}
})
})
I assume your children have same length. We can use 2 loop to group the element of children.
First loop to iterate the element of children.
Second loop to iterate the element of list.
Here is simple code to solve your case.
var listSize = list.length;
var childSize = list[0].children.length;
var expectedArrs = [];
for(var i=0;i<childSize;i++){
var groupByChild = [];
for(var j=0;j<listSize;j++){
groupByChild.push(list[j].children[i]);
}
expectedArrs.push(groupByChild);
}
console.log(expectedArrs);
The result of console:
[["apple", "tomato"], ["banana", "potato"], ["pear", "spinach"]]
It is resolved such that:
const result = []
for(let i = 0; i< List[0].children; i++){
const result1 = []
result1.push(list[0].children[i] )
result1.push(list[1].children[i])
result.push(result1)
}

Merge array with underscore by keys

I have 2 arrays :
[{id:1,name:"name"},{id:2,name:"name2"} ,{id:3,name:"name3"}]
[{id:1,date:"123"},{id:2,date:"456"}]
Array 1 should be updated only if the id is equal :
So the array 1 will looks like
It should not create a new array . Only update the array 1 based on array 2
[{id:1,name:"name",date:"123"},{id:2,name:"name2",date:"456"} ,{id:3,name:"name3"}]
I managed to do that with for loop on array2 and inside the for filter like the following :
._filter(array1,function(item){
If(item.id=array2.id)
Do smth and update the array1.date
})
How do I doing that in he best way ? Using underscore.js
You can do something like this:
Iterate over array1 and check if the id of each item exists in array2 by using the some() method.
var arr1 = [{id:1,name:"name"},{id:2,name:"name2"} ,{id:3,name:"name3"}];
var arr2 = [{id:1,date:"123"},{id:2,date:"456"}];
var missing = [];
arr1.forEach( (item1, i) => {
var isExist = arr2.some(item2 => item2.id === item1.id)
if(!isExist) {
missing.push(i);
}
})
missing.forEach(item => {
arr2.push(arr1[item]);
})
console.log(arr2);
reference for some()
Try this :
var a = [{id:1,name:"name"},{id:2,name:"name2"} ,{id:3,name:"name3"}] ;
var b = [{id:1,date:"123"},{id:2,date:"456"}] ;
var i = 0, j = 0 ;
while( i < a.length ) {
j = 0 ;
while( j < b.length) {
if ( a[i].id === b[j].id )
Object.assign( a[i] , b[j] );
j++;
}
i++;
}
console.log(a) ;
You can use forEach to iterate over the second array and use findIndex to get the matched element from first array. If the id matches then update the object in the first array
let arr1 = [{
id: 1,
name: "name"
}, {
id: 2,
name: "name2"
}, {
id: 3,
name: "name3"
}]
let arr2 = [{
id: 1,
date: "123"
}, {
id: 2,
date: "456"
}]
arr2.forEach(function(acc) {
let findArry1Index = arr1.findIndex(function(item) {
return item.id === acc.id;
});
if (findArry1Index !== -1) {
arr1[findArry1Index].date = acc.date;
}
});
console.log(arr1)
You can do it using native language like this:
const arr1 = [{id:1,name:"name"},{id:2,name:"name2"} ,{id:3,name:"name3"}];
const arr2 = [{id:1,date:"123"},{id:2,date:"456"}];
arr1.forEach((ele) => {
const match = arr2.find(item => ele.id === item.id) || {};
Object.assign(ele, match);
});
console.log(arr1);
var a = [{id:1,name:"name"},{id:2,name:"name2"} ,{id:3,name:"name3"}];
var b = [{id:1,date:"123"},{id:2,date:"456"}];
a = _.map(a, function(e) { return _.extend(e, _.findWhere(b, {id: e.id})); });
a results in:
0: {id: 1, name: "name", date: "123"}
1: {id: 2, name: "name2", date: "456"}
2: {id: 3, name: "name3"}
However, I guess this qualifies as "creating a new array"? Maybe it can serve as an inspiration though ¯\_(ツ)_/¯
You can use underscore's indexBy function to index your second array by id, and then simply use Object.assign(...) to update your first array's elements with their corresponding match by performing a lookup in the indexed elements object.
let arr1 = [{id:1, name:"name"}, {id:2, name:"name2"}, {id:3, name:"name3"}]
let arr2 = [{id:1, date:"123"}, {id:2, date:"456"}]
const arr2Groups = _.indexBy(arr2, e => e.id);
arr1.forEach(e => Object.assign(e, arr2Groups[e.id] || {}));
console.log(arr1);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>

Returns an array that consists of: one first item, two second items, tree third items etc. withour using loops

Im doing an assignment that requires that given array be converted to new one so that new array consists of one first item, two second items, tree third items etc. without using loops, just array specific methods. For example:
[] => []
[ 1 ] => [ 1 ]
[ 'a', 'b' ] => [ 'a', 'b','b' ]
[ 'a', 'b', 'c', null ] => [ 'a', 'b','b', 'c','c','c', null,null,null,null ]
I have solved it by using .map and recursions. Function looks like this:
function propagateItemsByPositionIndex(arr) {
let newArray = [];
let additions = 0;
arr.map(function (k, x) {
createArray(k, x);
additions = 0;
});
return newArray
function createArray(item, count) {
if (additions <= count) {
newArray.push(item);
++additions
createArray(item, count);
}
}
}
It feels like there should be much better way to do this.
One option is to use reduce, and concat to the array accumulator an array consisting of the iterated item repeated i + 1 times, where i is the item's index:
const transform = arr => arr.reduce((a, item, i) => (
a.concat(Array.from(
{ length: i + 1 },
() => item
))
), []);
console.log(transform([]));
console.log(transform([1]));
console.log(transform(['a', 'b']));
console.log(transform([ 'a', 'b', 'c', null ]));
You could use the upcoming Array#flatMap, which is a mapping function and flats the first level of the array values.
This works actually only in Chrome or FF (see Browser compatibility).
const theFn = array => array.flatMap((v, i) => Array.from({ length: i + 1 }).fill(v));
console.log(theFn([1, 2, 3, null]));
You can use Array.reduce() and use the index and value to create a new array with the specified length and filled with the desired value for each item, then use Array.push() and the spread operator to merge them all into one array, like this:
arr0 = [];
arr1 = [1];
arr2 = ['a', 'b'];
arr3 = ['a', 'b', 'c', null];
function propagateItemsByPositionIndex(arr) {
if (arr.length == 0 || arr.length == 1) return arr;
return arr.reduce((acc, v, i) => {
acc.push(...Array(i + 1).fill(v));
return acc;
}, []);
}
console.log(propagateItemsByPositionIndex(arr0));
console.log(propagateItemsByPositionIndex(arr1));
console.log(propagateItemsByPositionIndex(arr2));
console.log(propagateItemsByPositionIndex(arr3));
let array1 = [ 1 ]
let array2 = [ 'a', 'b' ]
let array3 = [ 'a', 'b', 'c', null ]
let array = [ 'a', 'b' ]
function transformArray(array){
return array.reduce(
(acc, curr, idx)=>{
//Creating an array of length equal to index+1
[...Array(idx+1)].forEach(item => acc[acc.length] = curr)
return acc
},
[]
)
}
console.log(transformArray(array1))
console.log(transformArray(array2))
console.log(transformArray(array3))

working with nested object

I think this is silly question and simple but I can't find any result in google and other resource
I have array of object like this
const myArr = [
[{
id: 1,
price: 200,
}, {
id: 2,
price: 900,
}, {
id: 3,
price: 100,
}],
[{
id: 5,
price: 100,
}]
];
In other word I have an array and my array contain some array and each of inner array contain some object inside them
arr [ [ {},{},{} ] , [ {} ] ]
now I want get two thing
count of all products ?
sum of all products ?
*(each object = one product)
Flatten to a single array by spreading into Array.concat().
Use Array.reduce() to get the sum.
The count is flattened array's length.
const myArr = [[{"id":1,"price":200},{"id":2,"price":900},{"id":3,"price":100}],[{"id":5,"price":100}]];
const flattened = [].concat(...myArr);
const count = flattened.length;
const sum = flattened.reduce((s, o) => s + o.price, 0);
console.log('count', count);
console.log('sum', sum);
You can use spread to flat the array:
var myArr = [
[
{
id:1,
price:200,
},
{
id:2,
price:900,
},
{
id:3,
price:100,
}
],
[
{
id:5,
price:100,
}
]
];
var arr = [].concat(...myArr);
console.log('Length: ', arr.length);
var sum = arr.reduce((m, o) => m + o.price, 0);
console.log('Sum: ', sum);
You can use concat to wrap all objects within one single array.
Apply length property in order to find out the number of objects in the array and then reduce method to get the sum.
const myArr = [ [ { id:1, price:200, }, { id:2, price:900, }, { id:3, price:100, } ], [ { id:5, price:100, } ] ];
arr = myArr.reduce((acc, arr) => acc.concat(arr), []);
sum = arr.reduce((acc, item) => acc + item.price, 0);
console.log('count ' + arr.length);
console.log('sum ' + sum)
ES6
You can also use reduce method of array to get the required result
reduce can be used to iterate through the array, adding the current element value to the sum of the previous element values.
DEMO
const myArr = [[{id: 1,price: 200,}, {id: 2,price: 900,}, {id: 3,price: 100,}],[{id: 5,price: 100,}]];
let result = myArr.reduce((r,v)=>{
r.count += v.length;
r.sum += v.reduce((total,{price}) => total+price,0);
return r;
},{count:0,sum:0})
console.log(result);
.as-console-wrapper {max-height: 100% !important;top: 0;}

es6 merge two array of objects and override the existing object

I have 2 array of objects:
const arr1 = [{'id':'1' 'value':'yes'}, {'id':'2', 'value':'no'}];
const arr2 = [{'id':'2', 'value':'yes'}];
So, if I try and merge these 2 arrays the result should be:
arrTemp = [{'id':'1', 'value':'yes'}, {'id':'2', 'value':'yes'}];
Basically, it should work similar to Object.assign(), but no matter what I try it does not work. Could anyone please help me in this ?
I modified the data structure. Is it possible to merge them now and get the output.
Thanks
This is how you can get the job done with ES6 spread, reduce and Object.values.
const arr1 = [{
'id': '1',
'value': 'yes'
}, {
'id': '2',
'value': 'no'
}];
const arr2 = [{
'id': '2',
'value': 'yes'
}];
const result = Object.values([...arr1, ...arr2].reduce((result, {
id,
...rest
}) => {
result[id] = {
...(result[id] || {}),
id,
...rest
};
return result;
}, {}));
console.log(result);
const result = Object.entries(Object.assign({}, ...arr1,...arr2)).map(([key, value]) => ({[key]:value}));
You could spread (...) the arrays into one resulting object ( via Object.assign) and then map its entries to an array again.
You could work with a valid ES6 data structure like a map for example:
const 1 = { 1: { string: 'yes' }, 2: { string: 'no' } }
const 2 = { 2: { string: 'yes' }, 3: { string: 'no' } }
const 3 = { ...1, ...2}
This will override your first argument with the second one or just combine them where possible.
Just try it out in your browser it's a lot easier and enhances performance since you will never have to use findById() which is an expensive operation.
In javascript, arrays are simply objects indexed by numbers starting from 0.
So when you use Object.assign on arr1 and arr2 you will override the first item in the arr1 with the first item in arr2 because they are both indexed under the key 0.
your result will be:
[
{ '2': 'yes' },
{ '2': 'no' }
]
(or in object syntax:)
{
0: { '2': 'yes' },
1: { '2': 'no' }
}
Instead of using arrays, you could create an object indexed by the number string (which is how you seem to be thinking of the array in any case).
So you could change your original data structure to make the job easier:
const arr1 = {
'1': 'yes',
'2': 'no'
};
const arr2 = {
'2': 'yes'
};
const result = Object.assign(arr1, arr2);
You could take a Map as reference to the new assigned object in the result array and build first a new array with a copy of the objects and then iterate the second array and update the objects with the same key.
var array1 = [{ 1: 'yes' }, { 2: 'no' }],
array2 = [{ 2: 'yes' }],
getKey = o => Object.keys(o)[0],
map = new Map,
result = array1.map(o => (k => map.set(k, Object.assign({}, o)).get(k))(getKey(o)));
array2.forEach(o => Object.assign(map.get(getKey(o)), o));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Array reduce could come in handy is this case. See example below:
[...arr1, ...arr2].reduce((acc, item) => {
const updated = acc.find(a => a.id === item.id)
if (!updated) {
acc.push(item)
} else {
const index = acc.indexOf(updated)
acc[index] = { ...item, ...acc[index] }
}
return acc
}, [])
simple way to add with exist array of object:
const arr1 = [{ "name":"John", "age":30, "car":"toyata" }];
const arr2 = [{ "name":"Ales", "age":40, "car":"Nissan" }];
Array.prototype.push.apply(arr1, arr2);
Result:=>
console.log(arr1)
For anyone finding this answer at a later point in time. There are a couple of ways that you could want this to work exactly, but you could filter all adjusted elements in the first array, and then combine it with the second array.
const arr3 = [...arr1.filter(item1 => !arr2.find(item2 => item1.id === item2.id)), ...arr2]
Alternatively, you could update the elements in the first array, and then filter them from the second array instead.
You cannot use array.prototype map because the key of arr1 and arr2 have the same value '2'.
You should use something like this
for (var i = 0, l = arr1.length; i < l; i++) {
var key = Object.keys(arr1[i]);
if (!arr2[key]) { arr2[key] = []; }
arr2[key].push(arr1[i][key]);
}
Regards

Categories