I need to update (or create new array) the id value in obj1 based on the id value in obj2. What is the best way to do this? I've tried with map and reduce but I wasn't successful. Any help will be appreciated.
const obj1 = [ { id: 1, name: 'foo' }, { id: 2, name: 'bar' } ]
const obj2 = { A: { id: 1, externalId:'AAA' }, B: { id: 2, externalId:'BBB' } }
outputExpected: [ { id: 'AAA', name: 'foo' }, { id: 'BBB', name: 'bar' } ]
Depending on the size of your arrays it may be more efficient to build a Map indexing obj2 ids (Map(2) { 1 => 'AAA', 2 => 'BBB', ... }) which can be used in mapping obj1.
const obj1 = [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' },];
const obj2 = { A: { id: 1, externalId: 'AAA' }, B: { id: 2, externalId: 'BBB' } };
const map = new Map(Object.values(obj2).map((o) => [o.id, o.externalId]));
const result = obj1.map(({ id, ...o }) => ({ id: map.get(id), ...o }));
console.log(result);
I'd suggest using Array.map(), searching obj2 for a matching id in each object in obj1, we'd use Array.find() to get the corresponding value in obj2.
const obj1 = [ { id: 1, name: 'foo' }, { id: 2, name: 'bar' } ]
const obj2 = { A: { id: 1, externalId:'AAA' }, B: { id: 2, externalId:'BBB' } }
const result = obj1.map((obj) => {
const match = Object.values(obj2).find(el => el.id === obj.id);
return { id: match.externalId, name: obj.name };
});
console.log('Result:', result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
Array.map works. Read values of obj2 as array to map to expected result. I suppose object keys A, B could be ignored.
const obj1 = [ { id: 1, name: 'foo' }, { id: 2, name: 'bar' } ];
const obj2 = { A: { id: 1, externalId:'AAA' }, B: { id: 2, externalId:'BBB' } };
const result = Object.values(obj2).map(({id, externalId}) => ({
id: externalId, name: obj1.find(item => item.id === id)?.name
}));
console.log(JSON.stringify(result));
Related
This question already has answers here:
group array of objects by id
(8 answers)
Closed 1 year ago.
I want to group the array of objects based on the key and concat all the grouped objects into a single array. GroupBy based on the id
example,
payload
[
{
id: 1,
name: 'a'
},
{
id: 1,
name: 'b'
},
{
id: 1,
name: 'c'
},
{
id: 2,
name: 'b'
},
{
id: 2,
name: 'c'
}
]
expected response
[
[
{
id: 1,
name: 'a'
},
{
id: 1,
name: 'b'
},
{
id: 1,
name: 'c'
}
],
[
{
id: 2,
name: 'b'
},
{
id: 2,
name: 'c'
}
]
]
All the matched elements are in the same array and all the arrays should be in a single array.
Array.redue will help
const input = [
{ id: 1, name: 'a' },
{ id: 1, name: 'b' },
{ id: 1, name: 'c' },
{ id: 2, name: 'b' },
{ id: 2, name: 'c' }
];
const output = input.reduce((acc, curr) => {
const node = acc.find(item => item.find(x => x.id === curr.id));
node ? node.push(curr) : acc.push([curr]);
return acc;
}, []);
console.log(output)
Extract the ids using Set so you have a unique set of them,
then loop over those ids and filter the original array based on it.
let objects = [
{
id: 1,
name: 'a'
},
{
id: 1,
name: 'b'
},
{
id: 1,
name: 'c'
},
{
id: 2,
name: 'b'
},
{
id: 2,
name: 'c'
}
]
let ids = [...new Set(objects.map(i => i.id))]
let result = ids.map(id => objects.filter(n => id === n.id))
console.log(result)
you can create a object with ids array by using Array.reduce method, and get the object values by Object.values
var s = [{
id: 1,
name: 'a'
},
{
id: 1,
name: 'b'
},
{
id: 1,
name: 'c'
},
{
id: 2,
name: 'b'
},
{
id: 2,
name: 'c'
}
];
//go through the input array and create a object with id's, group the values to gather
var ids = s.reduce((a, c) => {
//check object has the `id` property, if not create a property and assign empty array
if (!a[c.id])
a[c.id] = [];
//push the value into desidred object property
a[c.id].push(c)
//return the accumulator
return a;
}, {});
//get the grouped array as values
var outPut = Object.values(ids);
console.log(outPut);
1) You can easily achieve the result using Map and forEach easily
const arr = [
{
id: 1,
name: "a",
},
{
id: 1,
name: "b",
},
{
id: 1,
name: "c",
},
{
id: 2,
name: "b",
},
{
id: 2,
name: "c",
},
];
const map = new Map();
arr.forEach((o) => !map.has(o.id) ? map.set(o.id, [o]) : map.get(o.id).push(o));
const result = [...map.values()];
console.log(result);
/* This is not a part of answer. It is just to give the output full height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }
2) You can also achieve the result using reduce
const arr = [
{
id: 1,
name: "a",
},
{
id: 1,
name: "b",
},
{
id: 1,
name: "c",
},
{
id: 2,
name: "b",
},
{
id: 2,
name: "c",
},
];
const result = [...arr.reduce((map, curr) => {
!map.has(curr.id) ? map.set(curr.id, [curr]) : map.get(curr.id).push(curr);
return map;
}, new Map()).values()];
console.log(result);
/* This is not a part of answer. It is just to give the output full height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have a nested object of this structure.
mainObject: {
1: {
id: 1,
name:'alpha'
},
2: {
id: 2,
name:'beta'
},
3: {
id: 3,
name:'gamma'
}
}
i want to delete on object from this ,say 1 but that in an immutable way . I tried the following code.
const list =Object.values(mainObject)
const newList=list.filter((item)=>{item.id!=1})
newList.forEach((item)=>{
mainObject[item.id]={
id: item.id,
name:item.name
}
})
this is not working .What am i doing wrong. Thanks in advance.
You could use destruct assignment for deletion
const { 1: _, ...newObject } = mainObject
const mainObject = {
1: {
id: 1,
name: "alpha",
},
2: {
id: 2,
name: "beta",
},
3: {
id: 3,
name: "gamma",
},
}
const { 1: _, ...newObject } = mainObject
console.log(mainObject)
console.log(newObject)
Or clone mainObject in to an different object then apply deletion
const newObject = { ...mainObject }
delete newObject[1]
const mainObject = {
1: {
id: 1,
name: "alpha",
},
2: {
id: 2,
name: "beta",
},
3: {
id: 3,
name: "gamma",
},
}
const newObject = { ...mainObject }
delete newObject[1]
console.log(mainObject)
console.log(newObject)
Or transform object into array of key-value pairs, reject your delete pair and then transform it back to object
const newObject = Object.fromEntries(
Object.entries(mainObject).filter(([key]) => key !== "1")
)
const mainObject = {
1: {
id: 1,
name: "alpha",
},
2: {
id: 2,
name: "beta",
},
3: {
id: 3,
name: "gamma",
},
}
const newObject = Object.fromEntries(
Object.entries(mainObject).filter(([key]) => key !== "1")
)
console.log(mainObject)
console.log(newObject)
Reference
Destructuring assignment
Spread operator (...)
Object.prototype.entries()
Object.prototype.fromEntries()
You can use delete operator and spread syntax to achieve what you want.
For the object you have mentioned, let me walk you through it.
const mainObject = {
1: {
id: 1,
name: 'alpha',
},
2: {
id: 2,
name: 'beta',
},
3: {
id: 3,
name: 'gamma',
},
};
const newObj = { ...mainObject};
delete newObj["1"];
Read More on Spread syntax on MDN
Read More on delete operator on MDN
Hope it helps🙂
const mainObject = {
1: {
id: 1,
name: 'alpha',
},
2: {
id: 2,
name: 'beta',
},
3: {
id: 3,
name: 'gamma',
},
};
const newObj = { ...mainObject};
delete newObj["1"];
document.write(`<p>mainObject:</p> <pre> ${JSON.stringify(mainObject)} </pre>`)
document.write(`<p>newObj:</p> <pre> ${JSON.stringify(newObj)} </pre>`)
I have 2 arrays:
0: {id: 2, name: "TMA"}
1: {id: 3, name: "Hibbernate"}
0: {id: 1, name: "FB.DE"}
1: {id: 2, name: "TMA"}
2: {id: 3, name: "Hibbernate"}
3: {id: 4, name: "Event.it A"}
4: {id: 5, name: "Projket 2"}
5: {id: 6, name: "Projekt 1"}
I want to compare them and delete the objects with the id 2 and 3 cause both arrays have them and thats the similarity.
This is my Code so far:
const projectListOutput = projectsOfPersonArray.filter(project => data.includes(project));
console.log(projectListOutput);
But every time i run this projectListOutput is empty.
When using includes dont compare objects, Just build data as array of strings. Remaining code is similar to what you have.
arr1 = [
{ id: 2, name: "TMA" },
{ id: 3, name: "Hibbernate" },
];
arr2 = [
{ id: 1, name: "FB.DE" },
{ id: 2, name: "TMA" },
{ id: 3, name: "Hibbernate" },
{ id: 4, name: "Event.it A" },
{ id: 5, name: "Projket 2" },
{ id: 6, name: "Projekt 1" },
];
const data = arr1.map(({ id }) => id);
const result = arr2.filter(({ id }) => !data.includes(id));
console.log(result);
Your data array probably does not contain the exact same object references than projectsOfPersonArray. Look at the code below:
[{ foo: 'bar' }].includes({ foo: 'bar' });
// false
Objects look equal, but they don't share the same reference (= they're not the same).
It's safer to use includes with primitive values like numbers or strings. You can for example check the ids of your objects instead of the full objects.
You compare different objects, so every object is unique.
For filtering, you need to compare all properties or use a JSON string, if the order of properties is equal.
var exclude = [{ id: 2, name: "TMA" }, { id: 3, name: "Hibbernate" }],
data = [{ id: 2, name: "TMA" }, { id: 3, name: "Hibbernate" }, { id: 1, name: "FB.DE" }, { id: 2, name: "TMA" }, { id: 3, name: "Hibbernate" }, { id: 4, name: "Event.it A" }, { id: 5, name: "Projket 2" }, { id: 6, name: "Projekt 1" }],
result = data.filter(project =>
!exclude.some(item => JSON.stringify(item) === JSON.stringify(project))
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can do something similar to the next:
const source = [{
id: 1,
name: "FB.DE"
},
{
id: 2,
name: "TMA"
},
{
id: 3,
name: "Hibbernate"
},
{
id: 4,
name: "Event.it A"
},
{
id: 5,
name: "Projket 2"
},
{
id: 6,
name: "Projekt 1"
}
]
const toRemove = [{
id: 2,
name: "TMA"
},
{
id: 3,
name: "Hibbernate"
}
]
/**create object where keys is object "id" prop, and value is true**/
const toRemoveMap = toRemove.reduce((result, item) => ({
...result,
[item.id]: true
}), {})
const result = source.filter(item => !toRemoveMap[item.id])
You can make function from it:
function removeArrayDuplicates (sourceArray, duplicatesArray, accessor) {
const toRemoveMap = duplicatesArray.reduce((result, item) => ({
...result,
[item[accessor]]: true
}), {});
return sourceArray.filter(item => !toRemoveMap[item[accessor]])
}
removeArrayDuplicates(source, toRemove, 'id')
Or even better, you can make it work with a function instead of just property accessor:
function removeDuplicates (sourceArray, duplicatesArray, accessor) {
let objectSerializer = obj => obj[accessor];
if(typeof accessor === 'function') {
objectSerializer = accessor;
}
const toRemoveMap = duplicatesArray.reduce((result, item) => ({
...result,
[objectSerializer(item)]: true
}), {});
return sourceArray.filter(item => !toRemoveMap[objectSerializer(item)])
}
removeDuplicates(source, toRemove, (obj) => JSON.stringify(obj))
This function will help you merge two sorted arrays
var arr1 = [
{ id: 2, name: 'TMA' },
{ id: 3, name: 'Hibbernate' },
]
var arr2 = [
{ id: 1, name: 'FB.DE' },
{ id: 2, name: 'TMA' },
{ id: 3, name: 'Hibbernate' },
{ id: 4, name: 'Event.it A' },
{ id: 5, name: 'Projket 2' },
]
function mergeArray(array1, array2) {
var result = []
var firstArrayLen = array1.length
var secondArrayLen = array2.length
var i = 0 // index for first array
var j = 0 // index for second array
while (i < firstArrayLen || j < secondArrayLen) {
if (i === firstArrayLen) { // first array doesn't have any other members
while (j < secondArrayLen) { // we copy rest members of first array as a result
result.push(array2[j])
j++
}
} else if (j === secondArrayLen) { // second array doesn't have any other members
while (i < firstArrayLen) { // we copy the rest members of the first array to the result array
result.push(array1[i])
i++
}
} else if (array1[i].id < array2[j].id) {
result.push(array1[i])
i++
} else if (array1[i].id > array2[j].id) {
result.push(array2[j])
j++
} else {
result.push(array1[i])
i++
j++
}
}
return result
}
console.log(mergeArray(arr1,arr2));
Let's say we have an array that looks like this:
[
{
id: 0,
name: 'A'
},
{
id: 1,
name:'A'
},
{
id: 2,
name: 'C'
},
{
id: 3,
name: 'B'
},
{
id: 4,
name: 'B'
}
]
I want to keep only this objects that have the same value at 'name' key. So the output looks like this:
[
{
id: 0,
name: 'A'
},
{
id: 1,
name:'A'
},
{
id: 3,
name: 'B'
},
{
id: 4,
name: 'B'
}
]
I wanted to use lodash but I don't see any method for this case.
You can try something like this:
Idea:
Loop over the data and create a list of names with their count.
Loop over data again and filter out any object that has count < 2
var data = [{ id: 0, name: 'A' }, { id: 1, name: 'A' }, { id: 2, name: 'C' }, { id: 3, name: 'B' }, { id: 4, name: 'B' }];
var countList = data.reduce(function(p, c){
p[c.name] = (p[c.name] || 0) + 1;
return p;
}, {});
var result = data.filter(function(obj){
return countList[obj.name] > 1;
});
console.log(result)
A lodash approach that may (or may not) be easier to follow the steps of:
const originalArray = [{ id: 0, name: 'A' }, { id: 1, name: 'A' }, { id: 2, name: 'C' }, { id: 3, name: 'B' }, { id: 4, name: 'B' }];
const newArray =
_(originalArray)
.groupBy('name') // when names are the same => same group. this gets us an array of groups (arrays)
.filter(group => group.length == 2) // keep only the groups with two items in them
.flatten() // flatten array of arrays down to just one array
.value();
console.log(newArray)
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.11/lodash.min.js"></script>
A shorter solution with array.filter and array.some:
var data = [ { ... }, ... ]; // Your array
var newData = data.filter((elt, eltIndex) => data.some((sameNameElt, sameNameEltIndex) => sameNameElt.name === elt.name && sameNameEltIndex !== eltIndex));
console.log("new table: ", newTable);
You could use a hash table and a single loop for mapping the objects or just an empty array, then concat the result with an empty array.
var data = [{ id: 0, name: 'A' }, { id: 1, name: 'A' }, { id: 2, name: 'C' }, { id: 3, name: 'B' }, { id: 4, name: 'B' }],
hash = Object.create(null),
result = Array.prototype.concat.apply([], data.map(function (o, i) {
if (hash[o.name]) {
hash[o.name].update && hash[o.name].temp.push(hash[o.name].object);
hash[o.name].update = false;
return o;
}
hash[o.name] = { object: o, temp: [], update: true };
return hash[o.name].temp;
}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Let's say you've got three arrays of objects:
let a1 = [
{ id: 1, name: 'foo' },
{ id: 2, name: 'bar' },
{ id: 3, name: 'baz' }
]
let a2 = [
{ name: 'foo' },
{ name: 'bar' }
]
let a3 = [
{ name: 'bar' },
{ name: 'baz' }
]
The goal is to use a1 as a source, and add an id field to the elements of a2 and a3 with corresponding name fields in a1. What is an efficient way of accomplishing this? (Note: 'efficient' here meaning 'something more elegant than loops-within-loops-within-loops'.)
The result should look like this:
a2: [
{ id: 1, name: 'foo' },
{ id: 2, name: 'bar' }
]
a3: [
{ id: 2, name: 'bar' },
{ id: 3, name: 'baz' }
]
You could use a Map for referencing the id of a given name. Then assign.
var a1 = [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }, { id: 3, name: 'baz' }],
a2 = [{ name: 'foo' }, { name: 'bar' }],
a3 = [{ name: 'bar' }, { name: 'baz' }],
map = new Map(a1.map(o => [o.name, o.id]));
[a2, a3].forEach(a => a.forEach(o => o.id = map.get(o.name)));
console.log(a2);
console.log(a3);
.as-console-wrapper { max-height: 100% !important; top: 0; }
For an alternative answer, it could be like this.
It doesn't include loops and may be the shortest code in the answers.
const a1 = [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }, { id: 3, name: 'baz' }];
const a2 = [{ name: 'foo' }, { name: 'bar' }];
const a3 = [{ name: 'bar' }, { name: 'baz' }];
let f = x => a1.filter(a => x.some(y => y.name === a.name));
console.log(f(a2));
console.log(f(a3));
.as-console-wrapper { max-height: 100% !important; top: 0; }
a2.forEach((a2Elem) => a2Elem.id = a1.filter((a1Elem) => a1Elem.name === a2Elem.name)[0].id)
I'd first take the indexes of the given names, then just map the array to be merged into:
function combine(mergeInto, base) {
let indexes = base.map(e => e.name);
return mergeInto.map(e => ({
name: e.name,
id: base[indexes.indexOf(e.name)].id
}));
}
let a1 = [
{ id: 1, name: 'foo' },
{ id: 2, name: 'bar' },
{ id: 3, name: 'baz' }
]
let a2 = [
{ name: 'foo' },
{ name: 'bar' }
]
let a3 = [
{ name: 'bar' },
{ name: 'baz' }
]
function combine(mergeInto, base) {
let indexes = base.map(e => e.name);
return mergeInto.map(e => ({
name: e.name,
id: base[indexes.indexOf(e.name)].id
}));
}
console.log(combine(a3, a1));
A single loop proposal - create a hash table and then merge fields into the arrays - demo below:
let a1=[{id:1,name:'foo'},{id:2,name:'bar'},{id:3,name:'baz'}], a2=[{name:'foo'},{name:'bar'}], a3=[{name:'bar'},{name:'baz'}];
// create a hash table
let hash = a1.reduce(function(p,c){
p[c.name] = c;
return p;
},Object.create(null))
// merge the results
function merge(arr) {
Object.keys(arr).map(function(e){
arr[e]['id'] = hash[arr[e].name]['id'];
});
return arr;
}
console.log(merge(a2), merge(a3));
.as-console-wrapper{top:0;max-height:100%!important;}