Merging array of objects by different ids - javascript

I have such a case:
const ids = {
// id: parentId
"2": "1",
"3": "1"
}
const input = [
{
id: "1",
data: [1],
},
{
id: "2",
data: [2]
},
{
id: "3",
data: [3]
},
{
id: "4",
data: [4]
}
]
const output = [
{
id: "1",
data: [1,2,3]
},
{
id: "4",
data: [4]
}
]
With this ids I wanted to create some kind of config, which will determine which object in input array should have merged data (if there is no id-parentId pair defined, it should stay as it is). I belive code above is better explanation than this story. I tried with some mappings but without any nice result. This 'ids' array is an example, maybe there is a better way to define it so it will simplify the case? What do you think? I will be grateful for any hint

You can use reduce(). Create the output first as an object that uses id as the key. You can convert it to an array at the end with Object.values().
const ids = {
// id: parentId
"2": "1",
"3": "1"
}
const input = [{
id: "1",
data: [1],
},
{
id: "2",
data: [2]
},
{
id: "3",
data: [3]
},
{
id: "4",
data: [4]
}
]
const output = Object.values(input.reduce((acc, {
id,
data
}) => {
if (id in ids) { // replace id with parent
id = ids[id];
}
if (id in acc) {
acc[id].data = acc[id].data.concat(data);
} else {
acc[id] = {
id,
data};
}
return acc;
}, {}));
console.log(output);

You could take the references for same groups.
const
ids = { 2: "1", 3: "1" },
input = [{ id: "1", data: [1] }, { id: "2", data: [2] }, { id: "3", data: [3] }, { id: "4", data: [4] }],
output = Object.values(input.reduce((r, { id, data }) => {
id = ids[id] ?? id;
(r[id] ??= { id, data: [] }).data.push(...data);
return r;
}, {}));
console.log(output);
.as-console-wrapper { max-height: 100% !important; top: 0; }

You can try to cycle the input and modifying it in place, something like this should work:
const input = [
{
id: '1',
data: [1]
},
{
id: '2',
data: [2]
},
{
id: '3',
data: [3]
},
{
id: '4',
data: [4]
}
]
const config = {
2: '1',
3: '1'
}
const merge = ({ input, config } = {}) => {
for (const [childId, parentId] of Object.entries(config)) {
const childIndex = input.findIndex(i => i.id == childId)
const parent = input.find(i => i.id == parentId)
parent.data.push(...input[childIndex].data)
input.splice(childIndex, 1)
}
return input
}
console.log(merge({ input, config }))

you could do it like this:
let ids = { 2: "1", 3: "1" }
let input = [{ id: "1", data: [1] }, { id: "2", data: [2] }, { id: "3", data: [3] }, { id: "4", data: [4] }];
let keys = Object.keys(ids);
let objs = input.slice();//in order to not mutate the source input
const output = objs.map(currObj=>{
let currId = currObj.id;
if(keys.includes(currId)){
let changeObj = objs.find(obj=>obj.id==ids[currId])
changeObj.data.push(...currObj.data)
}
return currObj
})
console.log(output);
NOTE: I used slice() in order to not mutate the source input

You can use Array#reduce, Object.entries and Array#map methods as follows:
const ids = { "2": "1", "3": "1" },
input = [ { id: "1", data: [1], }, { id: "2", data: [2] }, { id: "3", data: [3] }, { id: "4", data: [4] } ],
output = Object.entries(
input.reduce(
(acc,{id,data}) =>
ids[id] ?
({...acc,[ids[id]]:[...(acc[ids[id]] || []),...data]}) :
({...acc,[id]:[...(acc[id] || []), ...data]}),{}
)
)
.map(([id,data]) => ({id,data}));
console.log( output );
//OUTPUT: [{"id":"1","data":[1,2,3]},{"id":"4","data":[4]}]

Related

Move objects in array where duplicates occur

I have an array of objects, each array has a key of name and then another array of objects:
const myArray = [ { name: "1", item: [{}] }, { name: "2", item: [{}] }, { name: "1", item: [{}] } ]
Now for example sometimes that name key will be the same, i want to be able to check if that name exists and if it does exist push the item into that array object and not into a new object.
The behaviour im getting is above but i would like:
const myArray = [ { name: "1", item: [{ item1, item2 etc }] }, { name: "2", item: [{}] }, { name: "3", item: [{}] } ]
Thanks so much in advance!
You can get the desired result using Array.reduce(), grouping by name.
If two objects in myArray share the same name, the item values are combined.
const myArray = [ { name: "1", item: [{ id: 1 }] }, { name: "2", item: [{ id: 2}] }, { name: "1", item: [{ id: 3}] } ]
const result = Object.values(myArray.reduce((acc, { name, item }) => {
acc[name] = acc[name] || { name, item: [] };
acc[name].item.push(...item);
return acc;
}, {}))
console.log('Result:', result)
.as-console-wrapper { max-height: 100% !important; }
Here's a solution using Array.prototype.reduce function.
const myArray = [ { name: "1", item: [{}] }, { name: "2", item: [{}] }, { name: "1", item: [{}] } ];
const output = myArray.reduce((acc, curr) => {
const index = acc.findIndex(pre => pre.name === curr.name);
if(index !== -1) {
acc[index].item = acc[index].item.concat(curr.item);
} else {
acc.push(curr);
}
return acc;
}, []);
console.log(output);

Find synced users from two arrays based on id and return array A with one column from array B

i am trying to find a list of synced users between two arrays (objArray1 & objArray2) and return the data from objArray1 along with 'aid' in Objarray2. I have the below code working, but i am trying to see if can return 'aid' from objArray2 as well in the below format.
Code sample below
// SystemA
const objArray1 = [
{ id: "1", name: "John" },
{ id: "2", name: "Jack" },
{ id: "3", name: "Sam" },
{ id: "4", name: "Bill" },
];
// SystemB
const objArray2 = [
{ id: "1", name: "John", aid:"uuud2905555" },
{ id: "3", name: "Sam" }, aid:"uuud29029303"
{ id: "5", name: "Bob" }, aid:"uuud29435454"
];
const array1IDs = objArray1.map((item) => {
return item.id
})
// Result: array1IDs = ["1", "2", "3", "4"];
const array2IDs = objArray2.map((item) => {
return item.id
})
// Result: array2IDs = ["1", "3", "5"];
// FIND SYNCED USERS
// Compare the id value of each item in objArray1 with each item of objArray2
// Return the ones that match.
const syncedUsers = objArray1.filter((item) => {
const found = objArray2.find((element) => element.id === item.id);
return found;
});
Required JSON Format, please note that all matching items should be returned from objArray1, with the exception of 'aid' from objArray2
{
"records": [
{
"id": {aid}, // from objArray2
"fields": {
"Name":{name}, // from objArray1
"sid": {id} // from objArray1
}
]
}
Presented below is one possible way to achieve the desired objective.
Code Snippet
// method to create result as "JSON"
const createJSON = (arr1, arr2) => (
// use "stringify" to transform JavaScript object into JSON
JSON.stringify({
// set-up the "records" prop
records: arr2
.filter( // filter to keep only those that have matching "id"
({ id }) => arr1.some(ob => ob.id === id)
)
.map( // de-structure & rename props to match desired objective
({ id : sid, name : Name, aid: id }) => ({
id,
fields: {Name, sid}
})
)
})
);
// SystemA
const objArray1 = [
{ id: "1", name: "John" },
{ id: "2", name: "Jack" },
{ id: "3", name: "Sam" },
{ id: "4", name: "Bill" },
];
// SystemB
const objArray2 = [
{ id: "1", name: "John", aid:"uuud2905555" },
{ id: "3", name: "Sam", aid:"uuud29029303" },
{ id: "5", name: "Bob", aid:"uuud29435454" },
];
console.log('result as a JSON: ', createJSON(objArray1, objArray2));
.as-console-wrapper { max-height: 100% !important; top: 0 }
Explanation
Inline comments added to the snippet above.
EDIT
Use name and id from array-1. Used restOfArr1Props to account for all other props of array-1 objects to be included.
const createJSON = (arr1, arr2) => (
JSON.stringify({
records: arr1
.filter(
({ id }) => arr2.some(ob => ob.id === id)
)
.map(
({ id : sid, name : Name, ...restOfArr1Props }) => ({
id: arr2.find(a2 => a2.id === sid)?.aid ?? 'missing aid',
fields: {
Name, sid, ...restOfArr1Props
},
})
)
})
);
// SystemA
const objArray1 = [
{ id: "1", name: "John", prop1: 'value11', prop2: 'value12' },
{ id: "2", name: "Jack", prop1: 'value21', prop2: 'value22' },
{ id: "3", name: "Sam", prop1: 'value31', prop2: 'value32' },
{ id: "4", name: "Bill", prop1: 'value41', prop2: 'value42' },
];
// SystemB
const objArray2 = [
{ id: "1", name: "John", aid:"uuud2905555" },
{ id: "3", name: "Sam", aid:"uuud29029303" },
{ id: "5", name: "Bob", aid:"uuud29435454" },
];
console.log('result as a JSON: ', createJSON(objArray1, objArray2));
.as-console-wrapper { max-height: 100% !important; top: 0 }
const objArray1 = [
{ id: '1', name: 'John', type: 'bully' },
{ id: '2', name: 'Jack', type: 'sporty' },
{ id: '3', name: 'Sam', type: 'kind' },
{ id: '4', name: 'Bill', type: 'poliet' },
];
const objArray2 = [
{ id: '1', name: 'John', aid: 'uuud2905555' },
{ id: '3', name: 'Sam', aid: 'uuud29029303' },
{ id: '5', name: 'Bob', aid: 'uuud29435454' },
];
const syncedUsers = { records: [] };
for (let user1 of objArray1) {
const foundUser = objArray2.find(user2 => user2.id === user1.id);
if (foundUser) {
syncedUsers.records.push({
id: foundUser.aid,
fields: { sid: user1.id, name: user1.name, ...user1 },
});
}
}
console.log(JSON.stringify(syncedUsers));

Return last value with recursion - Javascript

Hi all I have following data:
const section = {
fileds: [
{ id: "some Id-1", type: "user-1" },
{
child: [
{ id: "some Id-2", type: "user-2" },
{ fileds: [{ id: "kxf5", status: "pending" }] },
{ fileds: [{ id: "ed5t", status: "done" }] }
]
},
{
child: [
{ id: "some Id-3", type: "teacher" },
{ fileds: [{ id: "ccfr", status: null }] },
{ fileds: [{ id: "kdpt8", status: "inProgress" }] }
]
}
]
};
and following code:
const getLastIds = (arr) =>
arr.flatMap((obj) => {
const arrayArrs = Object.values(obj).filter((v) => Array.isArray(v));
const arrayVals = Object.entries(obj)
.filter(([k, v]) => typeof v === "string" && k === "id")
.map(([k, v]) => v);
return [...arrayVals, ...arrayArrs.flatMap((arr) => getLastIds(arr))];
});
console.log(getLastIds(section.fileds));
// output is (7) ["some Id-1", "some Id-2", "kxf5", "ed5t", "some Id-3", "ccfr", "kdpt8"]
My code doing following, it printing in new array all ids.
It's working but I don't need all ids.
I need to return only last id in array and I should use recursion.
The output should be
(4) [" "kxf5", "ed5t", "ccfr", "kdpt8"]
P.S. here is my code in codesandbox
Is there a way to solve this problem with recursion? Please help to fix this.
You can do it with reduce.
function getLastIds (value) {
return value.reduce((prev, cur) => {
if (cur.id) {
return [ ...prev, cur.id ];
} else {
let key = ('child' in cur) ? 'child' : 'fileds';
return [ ...prev, ...getLastIds (cur[key]) ]
}
}, []);
}
You could check if a certain key exists and take this property for mapping id if status exists.
const
getValues = data => {
const array = Object.values(data).find(Array.isArray);
return array
? array.flatMap(getValues)
: 'status' in data ? data.id : [];
},
section = { fileds: [{ id: "some Id-1", type: "user-1" }, { child: [{ id: "some Id-2", type: "user-2" }, { fileds: [{ id: "kxf5", status: "pending" }] }, { fileds: [{ id: "ed5t", status: "done" }] }] }, { child: [{ id: "some Id-3", type: "teacher" }, { fileds: [{ id: "ccfr", status: null }] }, { fileds: [{ id: "kdpt8", status: "inProgress" }] }] }] },
result = getValues(section);
console.log(result);

How to merge object with jquery by id

I need to be able to concatenate two JavaScript objects like the following:
let arr1 = [
{"0": { id: "abdc4051", date: "2017-01-24" }},
{"1": { id: "abdc4052", date: "2017-01-22" }}
];
let arr2 = [
{"0": { category: "Sport", data: {code: "abdc4051", name: "ab"} } },
{"1": { category: "Others", data: {code: "abdc4052", name: "abc"} } }
];
Does anyone have a script for this or know of a built in way to do this?
I want the date to be added in the data on arr2 with the condition code equal to id
Your object shape makes this harder than it should be. Are you certain you want the sequential properties in each object, or is that an artifact of logging/poor parsing?
You'll need to work around them if you actually need them, in the snippet below using Object.values() in creating a Map from arr1, and using Object.entries() in the final map() call on arr2 to store the sequential key and then reintroduce it in the return after the merge logic.
const
arr1 = [{ "0": { id: "abdc4051", date: "2017-01-24" } }, { "1": { id: "abdc4052", date: "2017-01-22" } }],
arr2 = [{ "0": { category: "Sport", data: { code: "abdc4051", name: "ab" } } }, { "1": { category: "Others", data: { code: "abdc4052", name: "abc" } } }],
// create map of dates: Map(2) { 'abdc4051' => '2017-01-24', 'abdc4052' => '2017-01-22' }
dateMap = new Map(arr1.map(o => {
const [{ id, date }] = Object.values(o);
return [id, date];
})),
// map over arr2, get date from Map and add it to 'data' if it exists
result = arr2.map(o => {
const [[k, _o]] = Object.entries(o);
const date = dateMap.get(_o.data.code);
return {
[k]: {
..._o,
data: { ..._o.data, ...(date ? { date } : {}) }
}
};
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you don't need the initial sequential keys the merge becomes much less verbose.
const
arr1 = [{ id: "abdc4051", date: "2017-01-24" }, { id: "abdc4052", date: "2017-01-22" }],
arr2 = [{ category: "Sport", data: { code: "abdc4051", name: "ab" } }, { category: "Others", data: { code: "abdc4052", name: "abc" } }],
dateMap = new Map(arr1.map(o => [o.id, o.date])),
result = arr2.map(o => (
{
...o,
data: { ...o.data, ...(dateMap.has(o.data.code) ? { date: dateMap.get(o.data.code) } : {}) }
}
));
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }

Setting array keys dynamically based on length

Given an array in this format:
[
[{
name: "name",
value: "My-name"
},
{
name: "qty",
value: "1"
},
{
name: "url",
value: "test.com"
},
{
name: "comment",
value: "my-comment"
}
],
[{
name: "name",
value: "My-name2"
},
{
name: "qty",
value: "3"
},
{
name: "url",
value: "test2.com"
}
],
[{
name: "name",
value: "My-name3"
},
{
name: "qty",
value: "1"
},
{
name: "url",
value: "test3.com"
},
{
name: "comment",
value: "my-comment3"
}
]
]
I'm looking to switch that to:
[
[
{ name: "My-name" },
{ qty: "1" },
{ url: "test.com" },
{ comment: "my-comment", }
],[
{ name: "My-name2" },
{ qty: "3" },
{ url: "test2.com",
],[
{ name: "My-name3", },
{ qty: "1", },
{ url: "test3.com", },
{ comment: "my-comment3", }
]
]
In other words, swapping out the array keys but maintaining the object structure within each array element.
I've tried looping over each element and can swap the keys out using something like:
newArray[iCount][item.name] = item.value;
However I'm then struggling to preserve the object order. Note that the comment field may or may not appear in the object.
With Array.map() function:
var arr = [
[{name:"name",value:"My-name"},{name:"qty",value:"1"},{name:"url",value:"test.com"},{name:"comment",value:"my-comment"}],
[{name:"name",value:"My-name2"},{name:"qty",value:"3"},{name:"url",value:"test2.com"}],
[{name:"name",value:"My-name3"},{name:"qty",value:"1"},{name:"url",value:"test3.com"},{name:"comment",value:"my-comment3"}]
],
result = arr.map(function(a){
return a.map(function(obj){
var o = {};
o[obj.name] = obj.value
return o;
});
});
console.log(result);
Check my moreBetterOutput value. I think will be better.
If you still need a result like your example in the question then you can check output value.
const input = [
[
{
name:"name",
value:"My-name"
},
{
name:"qty",
value:"1"
},
{
name:"url",
value:"test.com"
},
{
name:"comment",
value:"my-comment"
}
],
[
{
name:"name",
value:"My-name2"
},
{
name:"qty",
value:"3"
},
{
name:"url",
value:"test2.com"
}
],
[
{
name:"name",
value:"My-name3"
},
{
name:"qty",
value:"1"
},
{
name:"url",
value:"test3.com"
},
{
name:"comment",
value:"my-comment3"
}
]
]
const output = input.map(arr => arr.map(obj => ({[obj.name]: obj.value})))
const moreBetterOutput = output.map(arr => arr.reduce((acc, item, index) => {
acc[Object.keys(item)[0]] = item[Object.keys(item)[0]];
return acc;
}, {}) )
//console.log(output);
console.log(moreBetterOutput);
Another map function:
const result = array.map( subarray =>
Object.assign(...subarray.map( ({name, value}) => ({ [name] : value }) ))
);

Categories

Resources