I have an array containing objects where there is a rpId key in some of the objects. The goal is to separate/move the objects that return undefined to a separate array and remove them out of the first array.
e.g.:
results = [{id: 1}, {id: 2, rpId: 1076}, {id: 3}, {id: 4, rpId: 303}];
goal: results = [{id: 2, rpId: 1076}, {id: 4, rpId: 303}] and stations = [{id: 1}, {id: 3}]
My current approach can be seen below. As visible, I get a wrong array1 because it contains an object with a rpId, plus array2 returns the keys of the object and I'd like to read the entire object, not just the "undefined" of the key.
const array1 = [{id: 1}, {id: 2, rpId: 1076}, {id: 3}, {id: 4, rpId: 303}];
const array2 = [];
const mapping = array1.map((e) => e.rpId);
console.log("mapping",mapping);
mapping.forEach(function(elem, index){
elem === undefined ? array2.push(elem) && array1.splice(index, elem === undefined) && console.log(elem): console.log("defined", elem);
}),
console.log("1", array1); // [{ id: 2, rpId: 1076 }, { id: 3 }]
console.log("2", array2); // [undefined, undefined]
Just check if the rpId property is undefined in each element.
const array1 = [{id: 1}, {id: 2, rpId: 1076}, {id: 3}, {id: 4, rpId: 303}];
const array2 = [];
array1.forEach(function(elem, index){
if(elem.rpId === undefined)
array2.push(elem) && array1.splice(index, 1)
});
console.log(array1);
console.log(array2);
One can also use Array#filter or push elements into two separate arrays based on the condition for better performance.
const array1 = [{id: 1}, {id: 2, rpId: 1076}, {id: 3}, {id: 4, rpId: 303}];
const yes = [], no = [];
array1.forEach(elem=>(elem.rpId!==undefined?yes:no).push(elem));
console.log(yes);
console.log(no);
You can use filter too:
let results = [
{ id: 1 },
{ id: 2, rpId: 1076 },
{ id: 3 },
{ id: 4, rpId: 303 },
];
const stations = results.filter((c) => !c.rpId);
results = results.filter((c) => c.rpId);
console.log("stations", stations);
console.log("results", results);
const GiveItACreativeName = (arr) => {
const result = []
const stations = []
arr.forEach((el) => {
if('rpId' in el) result.push(el);
else stations.push(el);
});
return {result, stations}
}
console.log(
GiveItACreativeName([{id: 1}, {id: 2, rpId: 1076}, {id: 3}, {id: 4, rpId: 303}])
);
Related
I have 2 arrays of objects
var array1 = [
{id: 1, name:'fruit', rating:5},
{id: 4, name:'vegetable', rating: 3},
{id: 8, name:'meat', rating:1}
];
var array2 = [
{alimentId: 1, quantity: 2},
{alimentId: 4, quantity: 2},
{alimentId: 8, quantity: 4}
]
and I want to get a new the array1 such that
var array = [
{id: 1, name:'fruit'},
{id: 4, name:'vegetable'},
]
which has only the elements with quantity 2 matching the alimentId with the id.
I'm always getting confused with arrays and objects manipulations.. Please help
I believe the following code will solve your problem:
const func = (arr1, arr2) => {
return arr1.filter(obj => {
const objToCheck = arr2.filter(element => element.alimentId === obj.id);
return objToCheck[0].quantity === 2;
});
};
You also can send the wanted value(2) and the key name(quantity) as params.
var array1 = [
{id: 1, name:'fruit', rating:5},
{id: 4, name:'vegetable', rating: 3},
{id: 8, name:'meat', rating:1}
];
var array2 = [
{alimentId: 1, quantity: 2},
{alimentId: 4, quantity: 2},
{alimentId: 8, quantity: 4}
]
function filter(array1, array2) {
return array1
.filter(it => array2 // filter array1 by array2
.filter(it => it.quantity === 2) // filter your array2 by field quantity = 2
.map(it => it.alimentId) // pull out array of alimentId
.includes(it.id) // check array2.alimentId includes array1.id
)
}
console.log(filter(array1, array2))
use this function
const common_elements = (arr1, arr2, quantity) => {
let res = []
arr1.forEach(el1 => {
arr2.forEach(el2 => {
if(el1.id === el2.alimentId && el2.quantity === quantity) {
res.push(el1)
}
});
});
return res
}
You can do a reduce:
var array3 = array1.reduce((acc ,val ,index) => {
if (val.id=array2[index].alimentId) {
acc =[...acc, {id: val.id, name: val.name}]
}
return acc;
},[]);
var array1 = [
{id: 1, name:'fruit', rating:5},
{id: 4, name:'vegetable', rating: 3},
{id: 8, name:'meat', rating:1}
];
var array2 = [
{alimentId: 1, quantity: 2},
{alimentId: 4, quantity: 2},
{alimentId: 8, quantity: 4}
]
const commonArray = array2.filter(item => item.quantity === 2 && array1.find(el => el.id===item.alimentId));
console.log(commonArray)
I have an array as shown:
var arrOne = [{id: 3},{id: 8},{id: 12}];
And another array as shown:
var arrTwo = [
{id: 1, val: 'Adam'},
{id: 3, val: 'Bailey'},
{id: 8, val: 'Cathy'},
{id: 12, val: 'David'},
{id: 15, val: 'Earl'}
];
I want to iterate arrTwo based on arrOne, and get the val values out of arrTwo.
So the result should be:
var result = ['Bailey', 'cathy', 'David'];
Tried concatenating .map with .filter:
arrOne.map(arOne => arrTwo.filter(artwo => {
if(arOne.id === artwo.id) {
return artwo.val
} else {
return false;
}
}));
But it gives me all, and where it is false it adds false there, which I don't want.
Any ideas where I am going wrong will be appreciated.
Editing as per norbitrial's answer:
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [
{id: 1, val: 'Adam'},
{id: 3, val: 'Bailey'},
{id: 8, val: 'Cathy'},
{id: 12, val: 'David'},
{id: 15, val: 'Earl'}
];
const result = arrOne.map(({id}) => arrTwo.find(e => {
const someCond = someConditionaEval();
if(someCond && e.id === id) {
return e;
} else {
return false;
}
}).val); //this breaks
Using .map() and .find() combination:
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [{id: 1, val: 'Adam'}, {id: 3, val: 'Bailey'}, {id: 8, val: 'Cathy'}, {id: 12, val: 'David'}, {id: 15, val: 'Earl'}];
const result = arrOne.map(({id}) => arrTwo.find(e => e.id === id).val);
console.log(result);
I hope this helps!
You can use .filter() method on arrTwo and then using .includes() method get the filtered objects from arrTwo and then finally using .map() get only the val property values from each filtered object like:
var arrOne = [{id: 3},{id: 8},{id: 12}];
var arrTwo = [{id:1,val:"Adam"},{id:3,val:"Bailey"},{id:8,val:"Cathy"},{id:12,val:"David"},{id:15,val:"Earl"}];
var result = arrTwo.filter(a => arrOne.map(o=>o.id).includes(a.id)).map(o=>o.val)
console.log( result )
You could take an object with the values and then map the wanted values.
var arrOne = [{ id: 3 }, { id: 8 }, { id: 12 }],
arrTwo = [{ id: 1, val: 'Adam' }, { id: 3, val: 'Bailey' }, { id: 8, val: 'Cathy' }, { id: 12, val: 'David' }, { id: 15, val: 'Earl' }],
values = arrTwo.reduce((r, { id, val }) => (r[id] = val, r), {}),
result = arrOne.map(({ id }) => values[id]);
console.log(result);
Create a Map of val by id from arrTwo, and then map arrOne, and extract the val from the Map using the id.
Why I prefer creating a Map/dictionary (object) instead of using Array.map() with Array.find()?
Because of the complexity - Array.map() with Array.find(), for example, is O(n * m), while creating a Map and then using Array.map() to get the values is O(n + m). However, if you've got two small arrays, this shouldn't actually hurt actual performance.
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [{id: 1, val: 'Adam'}, {id: 3, val: 'Bailey'}, {id: 8, val: 'Cathy'}, {id: 12, val: 'David'}, {id: 15, val: 'Earl'}];
const valById = new Map(arrTwo.map(({ id, val }) => [id, val]));
const result = arrOne.map(o => valById.get(o.id));
console.log(result);
Build an object from arrTwo to gather val's in one iteration.
use map on arrOne and get val from above object.
const update = (arr1, arr2) => {
const all = Object.fromEntries(arr2.map(({ id, val }) => [id, val]));
return arr1.map(({ id }) => all[id]);
};
var arrOne = [{ id: 3 }, { id: 8 }, { id: 12 }];
var arrTwo = [
{ id: 1, val: "Adam" },
{ id: 3, val: "Bailey" },
{ id: 8, val: "Cathy" },
{ id: 12, val: "David" },
{ id: 15, val: "Earl" }
];
console.log(update(arrOne, arrTwo));
I want to remove same object from array by comparing 2 arrays.
Sample Data:
arr1 = [
{id: 1, name: "a"},
{id: 2, name: "b"},
{id: 3, name: "c"},
{id: 4, name: "d"},
];
arr2 = [
{id: 1, name: "a"},
{id: 4, name: "d"},
];
let newArray = []; // new array with with no same values it should be unique.
arr1.map((val, i)=>{
arr2.map((val2)=>{
if(val.id == val2.id){
console.log('Matched At: '+ i) // do nothing
}else{
newArray.push(val);
}
})
})
console.log(newArray); // e.g: [{id: 2, name: "b"}, {id: 3, name: "c"},];
Array.filter combined with not Array.some.
The trick here is also to not some,..
const arr1 = [
{id: 1, name: "a"},
{id: 2, name: "b"},
{id: 3, name: "c"},
{id: 4, name: "d"},
], arr2 = [
{id: 1, name: "a"},
{id: 4, name: "d"},
];
const newArray=arr1.filter(a=>!arr2.some(s=>s.id===a.id));
console.log(newArray);
.as-console-wrapper { max-height: 100% !important; top: 0; }
As mentioned in comments the question could be interpreted slightly differently. If you also want the unqiue items from arr2, you basically just do it twice and join. IOW: check what not in arr2 is in arr1, and then check what not in arr1 that's in arr2.
eg..
const notIn=(a,b)=>a.filter(f=>!b.some(s=>f.id===s.id));
const newArray=[...notIn(arr1, arr2), ...notIn(arr2, arr1)];
Update 2:
Time complexity, as mentioned by qiAlex there is loops within loops. Although some will short circuit on finding a match, if the dataset gets large things could slow down. This is were Set and Map comes in.
So to fix this using a Set.
const notIn=(a,b)=>a.filter(a=>!b.has(a.id));
const newArray=[
...notIn(arr1, new Set(arr2.map(m=>m.id))),
...notIn(arr2, new Set(arr1.map(m=>m.id)))
];
const isInArray = (arr, id, name) => arr.reduce((result, curr) => ((curr.name === name && curr.id === id) || result), false)
const newArray = arr1.reduce((result, curr) => (isInArray(arr2, curr.id, curr.name) ? result : result.concat(curr)), [])
You can update you code using filter() method, instead of using .map() method like:
const arr1 = [
{id: 1, name: "a"},
{id: 2, name: "b"},
{id: 3, name: "c"},
{id: 4, name: "d"},
], arr2 = [
{id: 1, name: "a"},
{id: 4, name: "d"},
];
let newArray = []; // new array with with no same values it should be unique.
newArray = arr1.filter(function(a) {
for(var i=0; i < arr2.length; i++){
if(a.id == arr2[i].id) return false;
}
return true;
});
console.log(newArray);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You check each element in first array whether its id lies in the second array by using Array.prototype.some. If the element is not present then only yield it.
const arr1 = [
{id: 1, name: "a"},
{id: 2, name: "b"},
{id: 3, name: "c"},
{id: 4, name: "d"},
];
const arr2 = [
{id: 1, name: "a"},
{id: 4, name: "d"},
];
const result = arr1.filter(x => !arr2.some(y => y.id === x.id));
console.log(result);
I think a simple comparer can works for getting differences and then concat them.
with this method you dont need to check which array is bigger.
arr1 = [ {id: 1, name: "a"}, {id: 2, name: "b"}, {id: 3, name: "c"}, {id: 4, name: "d"}];
arr2 = [ {id: 1, name: "a"}, {id: 4, name: "d"},];
function localComparer(b){
return function(a){
return b.filter(
function(item){
return item.id == a.id && item.name == a.name
}).length == 0;
}
}
var onlyInArr1 = arr1.filter(localComparer(arr2));
var onlyInArr2 = arr2.filter(localComparer(arr1));
console.log(onlyInArr1.concat(onlyInArr2));
We can filter values by checking whether some element is not contained in current array:
const result = arr1.reduce((a, c) => {
if (!arr2.some(a2 => a2.id === c.id))
a.push(c);
return a;
}, [])
An example:
let arr1 = [
{id: 1, name: "a"},
{id: 2, name: "b"},
{id: 3, name: "c"},
{id: 4, name: "d"},
];
let arr2 = [
{id: 1, name: "a"},
{id: 4, name: "d"},
];
const result = arr1.reduce((a, c) => {
if (!arr2.some(a2 => a2.id === c.id))
a.push(c);
return a;
}, [])
console.log(result);
Try this one -
const arr1 = [
{id: 1, name: "a"},
{id: 2, name: "b"},
{id: 3, name: "c"},
{id: 4, name: "d"},
];
const arr2 = [
{id: 1, name: "a"},
{id: 4, name: "d"},
];
const arr3 = [...arr1, ...arr2];
const mySubArray = _.uniq(arr3, 'id');
console.log(mySubArray);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>
So many loops in every answer.
Complexity of the code my answer is 2N,
Idea is:
to merge arrays.
first loop - mark duplicates somehow
second loop - filter duplicates out
arr1 = [
{id: 1, name: "a"},
{id: 2, name: "b"},
{id: 3, name: "c"},
{id: 4, name: "d"},
];
arr2 = [
{id: 1, name: "a"},
{id: 4, name: "d"},
];
let newArray = [...arr1, ...arr2].reduce((acc, item, index) => {
acc.items.push(item);
if (typeof acc.map[item.id] !== 'undefined') {
acc.items[acc.map[item.id]] = null;
acc.items[index] = null;
}
acc.map[item.id] = index;
return acc
}, {map: {}, items: []}).items.filter(item => !!item)
console.log(newArray);
I have a one dimensional array of objects and each object has an id and an id of its parent. In the initial array I have each element can have at most one child. So if the array looks like this:
{id: 3, parent: 5},
{id: 5, parent: null},
{id: 6, parent: 3},
{id: 1, parent: null},
{id: 4, parent: 7},
{id: 2, parent: 1},
{id: 7, parent: 2}
I need it to look similar to this:
{id: 5, parent: null, children: [
{id: 3, parent: 5},
{id: 6, parent: 3}
]},
{id: 1, parent: null, children: [
{id: 2, parent: 1},
{id: 7, parent: 2},
{id: 4, parent: 7},
]}
I think the way I did it uses too much loops. Is there a better way?
let items = [
{id: 3, parent: 5},
{id: 5, parent: null},
{id: 6, parent: 3},
{id: 1, parent: null},
{id: 4, parent: 7},
{id: 2, parent: 1},
{id: 7, parent: 2}
];
let itemsNew = [];
items = items.map(function(x){
return {id: x.id, parent: x.parent, children: []};
});
// new array with parents only
for(let i=items.length-1; i>=0; i--){
if(items[i].parent == null){
itemsNew.push(items[i]);
items.splice(i, 1);
}
}
for(let i=0; i<itemsNew.length; i++){
let childIndexes = findChildAll(itemsNew[i].id);
// sort the indexes so splicing the array wont misplace them
childIndexes.sort(function(a,b){
return b-a;
});
for(let j=0; j<childIndexes.length; j++){
itemsNew[i].children.push(items[childIndexes[j]]);
items.splice(childIndexes[j], 1);
}
}
// returns an array of indexes of all the element's children and their children
function findChildAll(parentId){
for(let i=0; i<items.length; i++){
if(items[i].parent == parentId){
let resultArray = findChildAll(items[i].id);
// is the result as an array add it to the index
if(resultArray) return [i].concat(resultArray);
// otherwise return just this index
return [i];
}
}
}
console.log(itemsNew);
You could simplify it a lot by utilizing filter:
for(const item of items)
item.children = items.filter(child => child.parent === item.id);
const parents = items.filter(item => !item.parent);
Or if there are lots of nodes, might be benefitial for performance to use a Map:
const itemsByID = new Map(items.map(item => [item.id, item]));
const parents = [];
for(const item of items) {
if(item.parent) {
const parent = itemsByID[item.parent];
if(!parent.children) parent.children = [];
parent.children.push(item);
} else parents.push(item);
}
finding the children could be slow if the array gets big because the function searches always in the full array
const withParent = items.filter((item) => item.parent !== null);
const getDeepIdsWithId = (id) => {
const result = [];
while (true) {
const i = withParent.find((item) => item.parent === id);
if (!i) {
break;
}
id = i.id;
result.push(i);
}
return result;
};
const parent = items.filter((item) => item.parent === null).map((item) => ({...item, children: getDeepIdsWithId(item.id)}));
I think you can take a recursive approach some like this:
const array1 = [{id: 3, parent: 5},
{id: 5, parent: null},
{id: 6, parent: 3},
{id: 1, parent: null},
{id: 4, parent: 7},
{id: 2, parent: 1},
{id: 7, parent: 2}];
const getChild = (origi, fathers) =>
origi.filter(
originEle => originEle.parent && fathers.some(f => f.id === originEle.parent)
)
const getChildren = (originArray, fathers) => {
let childs = getChild(originArray, fathers);
let continueFetching = childs && childs.length > 0;
while (continueFetching) {
const moreChilds = getChild(originArray, childs)
.filter(m => !childs.some(c => c.id === m.id))
if (
moreChilds &&
moreChilds.length > 0
) {
childs = childs.concat(moreChilds);
continueFetching = true;
} else {
continueFetching = false;
}
}
return childs;
}
const result = array1
.filter(
a1 => !a1.parent
)
.map(
aFather => ({
id: aFather.id,
parent: null,
children: getChildren(array1, [aFather])
})
)
Is it possible to concat two arrays with objects and let the second array overwrite the first array where they have the same id:
// array 1
[
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
]
// array 2:
[
{id: 1, name: "newFoo"},
{id: 4, name: "y"},
{id: 5, name: "z"}
]
// out:
[
{id: 1, name: "newFoo"}, // overwriten by array 2
{id: 2, name: "bar"}, // not changed (from array 1)
{id: 3, name: "baz"}, // not changed (from array 1)
{id: 4, name: "y"}, // added (from array 2)
{id: 5, name: "z"} // added (from array 2)
]
If it is possible I would like to do this without the use of third party libraries
var a = [
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
];
var b = [
{id: 1, name: "fooboo"},
{id: 4, name: "bar"},
{id: 5, name: "baz"}
];
/* iterate through each of b, if match found in a, extend with that of a. else push into b ...*/
b.forEach(m => {
var item = a.find(n => n.id === m.id);
if(item) { return Object.assign(item, m); }
a.push(m);
});
console.log(a);
You can do
let arr1 = [
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
]
let arr2 = [
{id: 1, name: "newFoo"},
{id: 4, name: "y"},
{id: 5, name: "z"}
]
let result = arr1.concat(arr2).reduce((a, b) => {
a[b.id] = b.name;
return a;
},{})
result = Object.keys(result).map(e => {
return {id : e, name : result[e]};
});
console.log(result);
Explanation
I am using the property of objects that they don't keep duplicate keys, so for an array concated together, I reduce it to an object with id as it's key and name as its value, hence overriding all duplicates. In the next step I converted this back into an array.
Check you my solution. There is no "rewrite", i just use a second array as base and don't write value if it has same id.
let a = [
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
];
let b = [
{id: 1, name: "newFoo"},
{id: 4, name: "y"},
{id: 5, name: "z"}
];
let duplicateId;
a.forEach(aitem => {
duplicateId = false;
b.forEach(bitem => {
if (aitem.id === bitem.id)
duplicateId = true;
});
if (!duplicateId)
b.push(aitem);
});
console.log(b);
Maybe you can use Object.assign and Object.entries to achieve, lets say:
const arr1 = [
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
]
const arr2 = [
{id: 1, name: "newFoo"},
{id: 4, name: "y"},
{id: 5, name: "z"}
]
const obj3 = Object.entries(Object.assign({}, ...arr1, arr2))
.map(([prop, value]) => ({[prop]:value}));
Example:
https://jsfiddle.net/0f75vLka/
Another option would be to convert arrays to map with id as key then merge the objects and then convert it back to array.
var arr1 = [
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
];
var arr2 = [
{id: 1, name: "newFoo"},
{id: 4, name: "y"},
{id: 5, name: "z"}
];
function arr2map(arr) {
var map = {};
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
map[item.id] = item;
}
return map;
}
function map2arr(map) {
var arr = [];
for (var i in map) {
arr.push(map[i]);
}
return arr;
}
var arr1m = arr2map(arr1);
var arr2m = arr2map(arr2);
var arr3m = map2arr( Object.assign({}, arr1m, arr2m) );
//output
alert(JSON.stringify(arr3m));