I've been tasked with creating an API connector between two systems. I needed to compare employees objects in SystemA and SystemB, then determine which employees would be new users, updating users, and users to be deleted in SystemB.
The goal is as follows:
Take an array of objects from SystemA and take an array of objects from SystemB. Then compare the two array of objects, and determine which objects need to be CREATED, UPDATED, and DELETED in SystemB.
If there are objects in SystemA's array that are not in SystemB's array, then those objects should be CREATED in SystemB system.
If there are objects in SystemA's array that are also in SystemB's array, then those objects in SystemB should be UPDATED.
If there are objects in SystemB's array that are not in SystemA's array, then those objects should be DELETED from SystemB's system.
This is what I came up with.
// 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" },
{ id: "3", name: "Sam" },
{ id: "5", name: "Bob" },
];
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;
});
// FIND NEW USERS
// Filter through each item in objArray1.
// If array2IDs array doesn't include a value matching the id of the current item,
// then return the current item
const newUsers = objArray1.filter((item) => {
if (!array2IDs.includes(item.id)) {
return item;
}
});
// FIND USERS TO DELETE
// Filter through each item in objArray2.
// If array1IDs array doesn't include a value matching the id of the current item,
// then return the current item
const usersToDelete = objArray2.filter((item) => {
if (!array1IDs.includes(item.id)) {
return item;
}
});
// Log results (or work with resulting arrays)
console.log("Synced Users:", syncedUsers);
console.log("New Users:", newUsers);
console.log("Users to delete:", usersToDelete);
i want to access the id 'qwsa221' without using array index but am only able to reach and output all of the array elements not a specific element.
i have tried using filter but couldnt figure out how to use it properly.
let lists = {
def453ed: [
{
id: "qwsa221",
name: "Mind"
},
{
id: "jwkh245",
name: "Space"
}
]
};
Use Object.keys() to get all the keys of the object and check the values in the array elements using . notation
let lists = {
def453ed: [{
id: "qwsa221",
name: "Mind"
},
{
id: "jwkh245",
name: "Space"
}
]
};
Object.keys(lists).forEach(function(e) {
lists[e].forEach(function(x) {
if (x.id == 'qwsa221')
console.log(x)
})
})
You can use Object.Keys method to iterate through all of the keys present.
You can also use filter, if there are multiple existence of id qwsa221
let lists = {
def453ed: [
{
id: "qwsa221",
name: "Mind"
},
{
id: "jwkh245",
name: "Space"
}
]
};
let l = Object.keys(lists)
.map(d => lists[d]
.find(el => el.id === "qwsa221"))
console.log(l)
you can do it like this, using find
let lists = {
def453ed: [
{
id: "qwsa221",
name: "Mind"
},
{
id: "jwkh245",
name: "Space"
}
]
};
console.log(
lists.def453ed // first get the array
.find( // find return the first entry where the callback returns true
el => el.id === "qwsa221"
)
)
here's a corrected version of your filter :
let lists = {def453ed: [{id: "qwsa221",name: "Mind"},{id: "jwkh245",name: "Space"}]};
// what you've done
const badResult = lists.def453ed.filter(id => id === "qwsa221");
/*
here id is the whole object
{
id: "qwsa221",
name: "Mind"
}
*/
console.log(badResult)
// the correct way
const goodResult = lists.def453ed.filter(el => el.id === "qwsa221");
console.log(goodResult)
// filter returns an array so you need to actually get the first entry
console.log(goodResult[0])
I'm trying to add into an array. I don't know how to traverse and add objects correctly.
I have data array:
const data = [
{
1: "Apple",
2: "Xiaomi"
}
];
const list = [];
data.forEach(function(key, value) {
console.log("key", key);
})
console.log(list)
I want this effect to be as follows:
list: [{
{
value: 1,
title: 'Apple'
},
{
value: 2,
title: 'Xiaomi'
}
}]
Your expected output is invalid. You can first retrieve all the values from the object with Object.values(). Then use Array.prototype.map() to form the array in the structure you want.
Try the following way:
const data = [
{
1: "Apple",
2: "Xiaomi"
}
];
const list = Object.values(data[0]).map((el,i) => ({value: i+1, title: el})) ;
console.log(list);
You can use the existing key of the object with Object.entries() like the following way:
const data = [
{
1: "Apple",
2: "Xiaomi"
}
];
const list = Object.entries(data[0]).map(item => ({value: item[0], title: item[1]}));
console.log(list);
I'll go ahead and make the assumption that data is an object of key/value pairs and you want to transform it to an array of objects.
// Assuming you have an object with key/value pairs.
const data = {
1: "Apple",
2: "Xiaomi"
};
// Convert the data object into an array by iterating over data's keys.
const list = Object.keys(data).map((key) => {
return {
value: key,
title: data[key]
}
});
console.log(list)
Output:
[
{
value: '1',
title: 'Apple'
},
{
value: '2',
title: 'Xiaomi'
}
]
If you actually need value to be numbers instead of strings, you can do it this way:
const list = Object.keys(data).map((key) => {
return {
value: Number(key),
title: data[key]
}
});
And if you are OK with using a more modern version of JavaScript (ECMAScript 2017) this works nicely:
const data = {
1: "Apple",
2: "Xiaomi"
};
// Using Object.entries gives you the key and value together.
const list = Object.entries(data).map(([value, title]) => {
return { value, title }
});
You could do something like this:
const data = ['Apple', 'Xiaomi'];
const result = data.map((item, index) => ({value: index, title: item}));
console.log(result);
If the idea is to turn key names into values and those are not necessarily autoincremented numbers you might want to look at Object.entries():
const data = {1: "Apple", 2: "Xiaomi"};
const res = Object.entries(data).map(entry => ({value: entry[0], title: entry[1]}));
console.log(res);
I am trying to find out the best / most efficient or most functional way to compare / merge / manipulate two arrays (lists) simultaneously in JS.
The example I give below is a simple example of the overall concept. In my current project, I deal with some very crazy list mapping, filtering, etc. with very large lists of objects.
As delinated below, my first idea (version1) on comparing lists would be to run through the first list (i.e. map), and in the anonymous/callback function, filter the second list to meet the criteria needed for the compare (match ids for example). This obviously works, as per version1 below.
I had a question performance-wise, as by this method on every iteration/call of map, the entire 2nd list gets filtered just to find that one item that matches the filter.
Also, the filter passes every other item in list2 which should be matched in list1. Meaning (as that sentence probably did not make sense):
list1.map list2.filter
id:1 [id:3,id:2,id:1]
^-match
id:2 [id:3,id:2,id:1]
^-match
id:3 [id:3,id:2,id:1]
^-match
Ideally on the first iteration of map (list1 id:1), when the filter encounters list2 id:3 (first item) it would just match it to list1 id:3
Thinking with the above concept (matching to a later id when it is encountered earlier, I came up with version2).
This makes list2 into a dictionary, and then looks up the value in any sequence by key.
const list1 = [
{id: '1',init:'init1'},
{id: '2',init:'init2'},
{id: '3',init:'init3'}
];
const list2 = [
{id: '2',data:'data2'},
{id: '3',data:'data3'},
{id: '4',data:'data4'}
];
/* ---------
* version 1
*/
const mergedV1 = list1.map(n => (
{...n,...list2.filter(f => f.id===n.id)[0]}
));
/* [
{"id": "1", "init": "init1"},
{"id": "2", "init": "init2", "data": "data2"},
{"id": "3", "init": "init3", "data": "data3"}
] */
/* ---------
* version 2
*/
const dictList2 = list2.reduce((dict,item) => (dict[item.id]=item,dict),{});
// does not handle duplicate ids but I think that's
// outside the context of this question.
const mergedV2 = list1.map(n => ({...n,...dictList2[n.id]}));
/* [
{"id": "1", "init": "init1"},
{"id": "2", "init": "init2", "data": "data2"},
{"id": "3", "init": "init3", "data": "data3"}
] */
JSON.stringify(mergedV1) === JSON.stringify(mergedV2);
// true
// and just for fun
const sqlLeftOuterJoinInJS = list1 => list2 => on => {
const dict = list2.reduce((dict,item) => (
dict[item[on]]=item,dict
),{});
return list1.map(n => ({...n,...dict[n[on]]}
))};
Obviously the above examples are pretty simple (merging two lists, each list having a length of 3). There are more complex instances that I am working with.
I don't know if there are some smarter (and ideally functional) techniques out there that I should be using.
You could take a closure over the wanted key for the group and a Map for collecting all objects.
function merge(key) {
var map = new Map;
return function (r, a) {
a.forEach(o => {
if (!map.has(o[key])) r.push(map.set(o[key], {}).get(o[key]));
Object.assign(map.get(o[key]), o);
});
return r;
};
}
const
list1 = [{ id: '1', init: 'init1' }, { id: '2', init: 'init2' }, { id: '3', init: 'init3' }],
list2 = [{ id: '2', data: 'data2' }, { id: '3', data: 'data3' }, { id: '4', data: 'data4' }],
result = [list1, list2].reduce(merge('id'), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Using filter for search is a misstep. Your instinct in version 2 is much better. Map and Set provide much faster lookup times.
Here's a decomposed approach. It should be pretty fast, but maybe not as fast as Nina's. She is a speed demon >_<
const merge = (...lists) =>
Array .from
( lists
.reduce (merge1, new Map)
.values ()
)
const merge1 = (cache, list) =>
list .reduce
( (cache, l) =>
cache .has (l.id)
? update (cache, l.id, l)
: insert (cache, l.id, l)
, cache
)
const insert = (cache, key, value) =>
cache .set (key, value)
const update = (cache, key, value) =>
cache .set
( key
, { ...cache .get (key)
, ...value
}
)
const list1 =
[{ id: '1', init: 'init1' }, { id: '2', init: 'init2' }, { id: '3', init: 'init3' }]
const list2 =
[{ id: '2', data: 'data2' }, { id: '3', data: 'data3' }, { id: '4', data: 'data4' }]
console .log (merge (list1, list2))
I'm offering this for completeness as I think Nina and #user633183 have offered most likely more efficient solutions.
If you wish to stick to your initial filter example, which is a max lookup N*M, and your arrays are mutable; you could consider reducing the set as you traverse through. In the old days shrinking the array had a huge impact on performance.
The general pattern today is to use a Map (or dict) as indicated in other answers, as it is both easy to understand and generally efficient.
Find and Resize
const list1 = [
{id: '1',init:'init1'},
{id: '2',init:'init2'},
{id: '3',init:'init3'}
];
const list2 = [
{id: '2',data:'data2'},
{id: '3',data:'data3'},
{id: '4',data:'data4'}
];
// combine by ID
let merged = list1.reduce((acc, obj)=>{
acc.push(obj);
// find index by ID
let foundIdx = list2.findIndex( el => el.id==obj.id );
// if found, store and remove from search
if ( foundIdx >= 0 ){
obj.data = list2[foundIdx].data;
list2.splice( foundIdx, 1 ); // shrink lookup array
}
return acc;
},[]);
// store remaining (if you want); i.e. {id:4,data:'data4'}
merged = merged.concat(list2)
console.log(merged);
.as-console-wrapper {
max-height: 100% !important;
top: 0;
}
I'm not sure whether I should mark this question as a duplicate because you phrased it differently. Anyway, here's my answer to that question copied verbatim. What you want is an equijoin:
const equijoin = (xs, ys, primary, foreign, sel) => {
const ix = xs.reduce((ix, row) => // loop through m items
ix.set(row[primary], row), // populate index for primary table
new Map); // create an index for primary table
return ys.map(row => // loop through n items
sel(ix.get(row[foreign]), // get corresponding row from primary
row)); // select only the columns you need
};
You can use it as follows:
const equijoin = (xs, ys, primary, foreign, sel) => {
const ix = xs.reduce((ix, row) => ix.set(row[primary], row), new Map);
return ys.map(row => sel(ix.get(row[foreign]), row));
};
const list1 = [
{ id: "1", init: "init1" },
{ id: "2", init: "init2" },
{ id: "3", init: "init3" }
];
const list2 = [
{ id: "2", data: "data2" },
{ id: "3", data: "data3" },
{ id: "4", data: "data4" }
];
const result = equijoin(list2, list1, "id", "id",
(row2, row1) => ({ ...row1, ...row2 }));
console.log(result);
It takes O(m + n) time to compute the answer using equijoin. However, if you already have an index then it'll only take O(n) time. Hence, if you plan to do multiple equijoins using the same tables then it might be worthwhile to abstract out the index.
Let's say I have an array as follows:
types = ['Old', 'New', 'Template'];
I need to convert it into an array of objects that looks like this:
[
{
id: 1,
name: 'Old'
},
{
id: 2,
name: 'New'
},
{
id: 3,
name: 'Template'
}
]
You can use map to iterate over the original array and create new objects.
let types = ['Old', 'New', 'Template'];
let objects = types.map((value, index) => {
return {
id: index + 1,
name: value
};
})
You can check a working example here.
The solution of above problem is the map() method of JavaScript or Type Script.
map() method creates a new array with the results of calling
a provided function on every element in the calling array.
let newArray = arr.map((currentvalue,index,array)=>{
return Element of array
});
/*map() method creates a new array with the results of calling
a provided function on every element in the calling array.*/
let types = [
'Old',
'New',
'Template'
];
/*
let newArray = arr.map((currentvalue,index,array)=>{
return Element of array
});
*/
let Obj = types.map((value, i) => {
let data = {
id: i + 1,
name: value
};
return data;
});
console.log("Obj", Obj);
Please follow following links:
TypeScript
JS-Fiddle
We can achieve the solution of above problem by for loop :
let types = [
"One",
"Two",
"Three"
];
let arr = [];
for (let i = 0; i < types.length; i++){
let data = {
id: i + 1,
name: types[i]
};
arr.push(data);
}
console.log("data", arr);