How to detect object differences between two arrays? - javascript

I'm trying to compare two arrays of objects and returns a list of updated objects. I don't want to use lodash just the javascript data structures and functions.
E.g:
I have a first array which named arr1 = [
{
name: 'attribute 1',
id: 12,
value: 40,
docs:[],
version: 1,
},
{
name: 'attribute 41',
id: 12,
value: 6,
version: 1,
}
]
And another array:
array2 = [
{
name: 'attribute 1',
attributeTypeId: 12,
value: 65,
docs: ['bla bla']
}
]
I'm trying to iterate through the two arrays and detect the differences and returns an array like that:
result = [
{
name: 'attribute 1',
id: 12,
value: 65,
docs:['bla bla'],
version: 1,
},
{
name: 'attribute 41',
id: 12,
value: 6,
version: 1,
}]
I wrote some uncomplete function (not optimized yet just a brute force solution):
const filterProperties = (e) => {
return e.toLowerCase() !== 'name' && e.toLowerCase() !== 'id'
}
// function sort
const sortProperties = (a, b) => a < b ? -1 : 1;
let result = []
attributesUpdate.forEach(attr => {
const attrProps = Object.getOwnPropertyNames(attr);
// iterate the attributes
for (let i = 0; i < attributes.length; i++) {
let attribute = attributes[i];
// check if the attribute to update has a different name or attributeTypeId
if (attribute.name !== attr.name) {
result = result.concat(attr);
}
// check if the attribute to update has the same name, id
// of the originalOne
if (attribute.name === attr.name && attribute.id=== attr.id) {
let obj = {
name: attribute.name,
id: attribute.id,
}
// get the properties of the attribute
const attributeProps = Object.getOwnPropertyNames(attribute);
// extract the name and id from the list
const filtredAttributeProps = attributeProps.filter(filterProperties);
const filteredattrProps = attrProps.filter(filterProperties);
// returns the length of each array of properties
const attrLength = filteredattrProps.length;
const attributeLength = filtredAttributeProps.length;
if (attrLength === attributeLength) {
for (let j = 0; j < attrLength; j++) {
const propName = filteredattrProps[j];
obj[propName] = attr[propName];
}
result = result.filter(e => e.name === attr.name
&& e.id=== attr.id)
.map(e => Object.assign(e, {obj}))
}
if (attrLength !== attributeLength) {
// sort the array of properties
const sortedAttrProps = filteredattrProps.sort(sortProperties);
const sortedAttributeProps = filtredAttributeProps.sort(sortProperties);
// check the shortest object
const min = attrLength < attributeLength ? attrLength : attributeLength;
// get the biggest object
const longestObjProps = attrLength === min ? sortedAttributeProps : sortedAttrProps;
const longestObj = attrLength === min ? attribute : attr
const shortestProps = attrLength === min ? sortedAttrProps: sortedAttributeProps;
const shortestObj = attrLength === min ? attr : attribute
// fill the object with attr properties
for(let j = 0; j < min; j++) {
const propName = shortestProps[j];
obj[propName] = shortestObj[propName];
}
// fill the remaining properties in the object
const remainingProperties = longestObjProps.filter(e => !shortestProps.includes(e));
for (let j = 0; j < remainingProperties.length; j++) {
const propName = remainingProperties[j];
obj[propName] = longestObj[propName]
}
if (!result.length || result.filter(e => e.name !== attr.name &&
e.id!== attr.id).length === 0) {
result.concat(obj);
}
}
}
}
})
console.log('result: ', result);
I got such a result :
[
{
name: 'attribute 1',
attributeTypeId: 12,
value: 65,
docs: ['bla bla']
}
]
How can I fix this code to get the desired results? I hope that my question will not be downvoted. Any suggestion will be welcome.

What this code does is loop through the objects in array2, and then when it finds that there is a matching name/id in arr1, it simply updates the properties of that object. If not found, it will add the object to arr1.
arr1 = [{
name: 'attribute 1',
id: 12,
value: 40,
docs: [],
version: 1,
},
{
name: 'attribute 41',
id: 12,
value: 6,
version: 1,
}
];
array2 = [{
name: 'attribute 1',
attributeTypeId: 12,
value: 65,
docs: ['bla bla']
}];
updateArray(arr1, array2);
console.log(arr1);
function updateArray(arrayToUpdate, dataToUpdateWith) {
dataToUpdateWith.forEach(function(obj) {
var objToUpdate = checkIfNameIdExists(arrayToUpdate, obj.name, obj.attributeTypeId);
if (objToUpdate === false) {
objToUpdate = obj;
arrayToUpdate.push(objToUpdate);
} else {
for (var prop in obj) {
if (objToUpdate.hasOwnProperty(prop)) {
var nameInFinalObject = prop;
if (prop === "attributeTypeId") {
nameInFinalObject = "id";
}
objToUpdate[nameInFinalObject] = obj[prop];
}
}
}
});
}
function checkIfNameIdExists(arrOfObj, name, id) {
if (name === null) {
return false;
}
var output = false;
arrOfObj.forEach(function(obj) {
if (obj.name === name) {
output = obj;
return true;
}
});
return output;
}

Assumptions:
The values in each of the objects are same type and values are not nested so there is a need to recursively traverse the tree to compare equality etc.
The first array is the source and the subsequent (with the same name) is the mutated form.
We are not handling removals of properties from the source object. From what is given by the OP we are only accounting for value changes.
const d1 = [{ name: 'attribute 1', id: 12, value: 40, docs: [], version: 1, }, { name: 'attribute 41', id: 12, value: 6, version: 1, } ]
const d2 = [{ name: 'attribute 1', attributeTypeId: 12, value: 65, docs: ['bla bla'] }]
const isChanged = (a, b) =>
Array.isArray(a) ? !a.every(x => b.includes(x)) : a !== b
const compare = (o1, o2) => Object.entries(o1).reduce((r, [k,v]) => {
if(k in o2 && isChanged(o2[k], v))
Object.assign(r, {[k]: o2[k]})
return r
}, o1)
const group = (a, b) => [...a, ...b].reduce((r,c) =>
(r[c.name] = [...r[c.name] || [], c], r), {})
const result = Object.values(group(d1,d2)).reduce((r,c) =>
(r.push(c.length == 2 ? compare(...c) : c[0]), r), [])
console.log(result)
The idea is to merge the objects in one array, group them by name and if there ware any changes the groups with length of 2 would be compared by the compare function. Otherwise just added to the end result.

Related

How to compare two arrays and return another one?

How to compare two arrays and return another one?
I'm trying to compare two arrays to compare records by id and then render a new array
const arr1 = [
{ id: 1, title: "Admin" },
{ id: 2, title: "Vip" }
];
const arr2 = [
{
id: 1,
root: 1
},
{
id: 2,
root: 0
}
];
let intersection = arr1.filter(({ id }) => arr2.includes(id));
need:
const needArr = [
{ id: 1, title: "Admin", root: 1 },
{ id: 2, title: "Vip", root: 0 }
];
You could make use of map() and find() and iterate over the first array arr1:
const needArr = arr1.map(entry => {
const root = arr2.find(arr2Entry => entry.id === arr2Entry.id)?.root
return {...entry, root: root}
} )
The root property will be set to undefined for each entry in the needArr result if there is no entry with the same id in arr2 as in arr1.
Something like this could work,
const giveNew = (a, b) => {
let shorter, longer;
if(a.length>b.length){
longer = a;
shorter = b;
} else {
longer = b;
shorter = a;
}
return longer.map((v, i)=> {
const matched = shorter.find(val=> val.id === v.id);
if(matched){
return {
...v, ...matched
}
}
})
}
Assuming there's a 1:1 relationship between the arrays - map over one of the arrays, find the corresponding object in the other array by its id, and then return a new updated object.
const arr1=[{id:1,title:"Admin"},{id:2,title:"Vip"}],arr2=[{id:1,root:1},{id:2,root:0}];
const out = arr2.map(obj => {
return {
...arr1.find(inner => inner.id === obj.id),
root: obj.root
};
});
console.log(out);
As is pointed out in Merge two array of objects based on a key, you can do this:
let intersection = arr1.map(item => ({...item, ...arr2.find(item2 => item.id === item2.id)}));
I tried this worked.
const arr1 = [
{ id: 1, title: "Admin" },
{ id: 2, title: "Vip"}
];
const arr2 = [
{
id: 1,
root: 1
},
{
id: 2,
root: 0
}
];
for(var i=0 ;i < arr2.length; i++)
{
objIndex = arr1.findIndex((obj => obj.id == arr2[i].id));
arr1[objIndex].root = arr2[i].root;
}
console.log(arr1);
Hope this satisfies your use case. This also works in the case where there is no 1:1 mappings.
const arr1 = [
{ id: 1, title: "Admin" , root: 0 },
{ id: 2, title: "Vip" , root: 0 },
{ id: 100, title: "NotExistInArr2" , root: 0 }
];
const arr2 = [
{
id: 1,
root: 1
},
{
id: 2,
root: 0
},
{
id: 200,
root: 0
}
];
const consolidatedIds = arr1.map(a => a.id).concat(arr2.map(a => a.id));
//console.log(consolidatedIds);
const consolidatedDedupedIds = arrayUnique(consolidatedIds);
//console.log(consolidatedDedupedIds);
const needArr = consolidatedDedupedIds.map(entry => {
const arr1Item = arr1.find(arr1Entry => entry === arr1Entry.id);
const arr2Item = arr2.find(arr2Entry => entry === arr2Entry.id);
return {...arr1Item, ...arr2Item}
} )
console.log(needArr)
//--- utility function
function arrayUnique(array) {
var a = array.concat();
for(var i=0; i<a.length; ++i) {
for(var j=i+1; j<a.length; ++j) {
if(a[i] === a[j])
a.splice(j--, 1);
}
}
return a;
}
Note: improvised version of other questions, inspired from other answers

Merge Array of same level

I have an array which I need to combine with comma-separated of the same level and form a new array.
Input:
let arr = [
[{ LEVEL: 1, NAME: 'Mark' }, { LEVEL: 1, NAME: 'Adams' }, { LEVEL: 2, NAME: 'Robin' }],
[{ LEVEL: 3, NAME: 'Williams' }],
[{ LEVEL: 4, NAME: 'Matthew' }, { LEVEL: 4, NAME: 'Robert' }],
];
Output
[
[{ LEVEL: 1, NAME: 'Mark,Adams' }, { LEVEL: 2, NAME: 'Robin' }],
[{ LEVEL: 3, NAME: 'Williams' }],
[{ LEVEL: 4, NAME: 'Matthew,Robert' }],
];
I tried with the following code but not getting the correct result
let finalArr = [];
arr.forEach(o => {
let temp = finalArr.find(x => {
if (x && x.LEVEL === o.LEVEL) {
x.NAME += ', ' + o.NAME;
return true;
}
if (!temp) finalArr.push(o);
});
});
console.log(finalArr);
You could map the outer array and reduce the inner array by finding the same level and add NAME, if found. Otherwise create a new object.
var data = [[{ LEVEL: 1, NAME: "Mark" }, { LEVEL: 1, NAME: "Adams" }, { LEVEL: 2, NAME: "Robin"}], [{ LEVEL: 3, NAME: "Williams" }], [{ LEVEL: 4, NAME: "Matthew" }, { LEVEL: 4, NAME: "Robert" }]],
result = data.map(a => a.reduce((r, { LEVEL, NAME }) => {
var temp = r.find(q => q.LEVEL === LEVEL);
if (temp) temp.NAME += ',' + NAME;
else r.push({ LEVEL, NAME });
return r;
}, []));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Assuming you only want to merge within the same array and not across arrays, and assuming there aren't all that many entries (e.g., fewer than several hundred thousand), the simple thing is to build a new array checking to see if it already has the same level in it:
let result = arr.map(entry => {
let newEntry = [];
for (const {LEVEL, NAME} of entry) {
const existing = newEntry.find(e => e.LEVEL === LEVEL);
if (existing) {
existing.NAME += "," + NAME;
} else {
newEntry.push({LEVEL, NAME});
}
}
return newEntry;
});
let arr= [
[{"LEVEL":1,"NAME":"Mark"},
{"LEVEL":1,"NAME":"Adams"},
{"LEVEL":2,"NAME":"Robin"} ],
[{"LEVEL":3,"NAME":"Williams"}],
[{"LEVEL":4,"NAME":"Matthew"},
{"LEVEL":4,"NAME":"Robert"}]
];
let result = arr.map(entry => {
let newEntry = [];
for (const {LEVEL, NAME} of entry) {
const existing = newEntry.find(e => e.LEVEL === LEVEL);
if (existing) {
existing.NAME += "," + NAME;
} else {
newEntry.push({LEVEL, NAME});
}
}
return newEntry;
});
console.log(result);
If the nested arrays can be truly massively long, you'd want to build a map rather than doing the linear search (.find) each time.
I'd try to do as much of this in constant time as possible.
var m = new Map();
array.forEach( refine.bind(m) );
function refine({ LABEL, NAME }) {
var o = this.get(NAME)
, has = !!o
, name = NAME
;
if (has) name = `${NAME}, ${o.NAME}`;
this.delete(NAME);
this.set(name, { NAME: name, LABEL });
}
var result = Array.from( m.values() );
I haven't tested this as I wrote it on my phone at the airport, but this should at least convey the approach I would advise.
EDIT
Well... looks like the question was edited... So... I'd recommend adding a check at the top of the function to see if it's an array and, if so, call refine with an early return. Something like:
var m = new Map();
array.forEach( refine.bind(m) );
function refine(item) {
var { LABEL, NAME } = item;
if (!NAME) return item.forEach( refine.bind(this) ); // assume array
var o = this.get(NAME)
, has = !!o
, name = NAME
;
if (has) name = `${NAME}, ${o.NAME}`;
this.delete(NAME);
this.set(name, { NAME: name, LABEL });
}
var result = Array.from( m.values() );
That way, it should work with both your original question and your edit.
EDIT
Looks like the question changed again... I give up.
Map the array values: every element to an intermediate object, then create the desired object from the resulting entries:
const basicArr = [
[{"LEVEL":1,"NAME":"Mark"},
{"LEVEL":1,"NAME":"Adams"},
{"LEVEL":2,"NAME":"Robin"} ],
[{"LEVEL":3,"NAME":"Williams"}],
[{"LEVEL":4,"NAME":"Matthew"},
{"LEVEL":4,"NAME":"Robert"}]
];
const leveled = basicArr.map( val => {
let obj = {};
val.forEach(v => {
obj[v.LEVEL] = obj[v.LEVEL] || {NAME: []};
obj[v.LEVEL].NAME = obj[v.LEVEL].NAME.concat(v.NAME);
});
return Object.entries(obj)
.map( ([key, val]) => ({LEVEL: +key, NAME: val.NAME.join(", ")}));
}
);
console.log(leveled);
.as-console-wrapper { top: 0; max-height: 100% !important; }
if you want to flatten all levels
const basicArr = [
[{"LEVEL":1,"NAME":"Mark"},
{"LEVEL":1,"NAME":"Adams"},
{"LEVEL":2,"NAME":"Robin"} ],
[{"LEVEL":3,"NAME":"Williams"}],
[{"LEVEL":4,"NAME":"Matthew"},
{"LEVEL":4,"NAME":"Robert"},
{"LEVEL":2,"NAME":"Cynthia"}],
[{"LEVEL":3,"NAME":"Jean"},
{"LEVEL":4,"NAME":"Martha"},
{"LEVEL":2,"NAME":"Jeff"}],
];
const leveled = basicArr.map( val => Object.entries (
val.reduce( (acc, val) => {
acc[val.LEVEL] = acc[val.LEVEL] || {NAME: []};
acc[val.LEVEL].NAME = acc[val.LEVEL].NAME.concat(val.NAME);
return acc;
}, {}))
.map( ([key, val]) => ({LEVEL: +key, NAME: val.NAME.join(", ")})) )
.flat() // (use .reduce((acc, val) => acc.concat(val), []) for IE/Edge)
.reduce( (acc, val) => {
const exists = acc.filter(x => x.LEVEL === val.LEVEL);
if (exists.length) {
exists[0].NAME = `${val.NAME}, ${exists.map(v => v.NAME).join(", ")}`;
return acc;
}
return [... acc, val];
}, [] );
console.log(leveled);
.as-console-wrapper { top: 0; max-height: 100% !important; }
ES6 way:
let say attributes is multidimensional array having multimple entries which need to combine like following:
let combinedArray = [];
attributes.map( attributes => {
combined = combinedArray.concat(...attributes);
});

Javascript: Remove duplicates in an array by compare function

I have an array with objects like this:
const array = [
{name:'obj1', address: 987, id: '123', location: 'zyx' },
{name:'obj2', address: 654, id: '456', location: 'wvu'},
{name:'obj3', address: 321, id: '123', location: 'zyx'}
];
and I want to remove the duplicates with a function to compare them:
const compareObjects = (a, b) => {
return a.id === b.id && a.location === b.location;
}
The function only compares the relevant properties of the objects.
How can I remove the duplicates from the array with this function?
Edit: To clarify, I want to use a function to compare some properties of the object and not the whole object.
You could reduce the array by checking the object of the temporary result set.
const
array = [{ name:'obj1', address: 987, id: '123', location: 'zyx' }, { name:'obj2', address: 654, id: '456', location: 'wvu' }, { name:'obj3', address: 321, id: '123', location: 'zyx' }],
compareObjects = (a, b) => a.id === b.id && a.location === b.location,
result = array.reduce((r, o) => {
if (!r.some(compareObjects.bind(null, o))) {
r.push(o);
}
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The obvious solution is to compare each element against each other element (except for itself):
const result = array.filter((el, i) => !arr.some((el2, i2) => i < i2 && compareObjects(el, el2));
However that is O(n²) which will get very slow for large datasets, in that case hashtables help you:
const dupes = new Set;
const key = el => el.id + "|" + el.location;
const result = array.filter(it => !dupes.has(key(el)) && dupes.add(key(el)));
That is O(n) (aka super fast, but consumes more memory).
You can try a function like below
function unique(array , compareObjects){
array.sort(compareObjects);
for(var i = 1; i < array.length; ){
if( compareObjects(array[i-1], array[i]) === 0){
array.splice(i, 1);
} else {
i++;
}
}
return array;
}

how to count duplicate values object to be a value of object

how to count the value of object in new object values
lets say that i have json like this :
let data = [{
no: 3,
name: 'drink'
},
{
no: 90,
name: 'eat'
},
{
no: 20,
name: 'swim'
}
];
if i have the user pick no in arrays : [3,3,3,3,3,3,3,3,3,3,3,90,20,20,20,20]
so the output should be an array
[
{
num: 3,
total: 11
},
{
num: 90,
total: 1
},
{
num:20,
total: 4
}
];
I would like to know how to do this with a for/of loop
Here is the code I've attempted:
let obj = [];
for (i of arr){
for (j of data){
let innerObj={};
innerObj.num = i
obj.push(innerObj)
}
}
const data = [{"no":3,"name":"drink"},{"no":90,"name":"eat"},{"no":20,"name":"swim"}];
const arr = [3,3,3,3,3,3,3,3,3,3,3,20,20,20,20,80,80];
const lookup = {};
// Loop over the duplicate array and create an
// object that contains the totals
for (let el of arr) {
// If the key doesn't exist set it to zero,
// otherwise add 1 to it
lookup[el] = (lookup[el] || 0) + 1;
}
const out = [];
// Then loop over the data updating the objects
// with the totals found in the lookup object
for (let obj of data) {
lookup[obj.no] && out.push({
no: obj.no,
total: lookup[obj.no]
});
}
document.querySelector('#lookup').textContent = JSON.stringify(lookup, null, 2);
document.querySelector('#out').textContent = JSON.stringify(out, null, 2);
<h3>Lookup output</h3>
<pre id="lookup"></pre>
<h3>Main output</h3>
<pre id="out"></pre>
Perhaps something like this? You can map the existing data array and attach filtered array counts to each array object.
let data = [
{
no: 3,
name: 'drink'
},
{
no:90,
name: 'eat'
},
{
no:20,
name: 'swim'
}
]
const test = [3,3,3,3,3,3,3,3,3,3,3,90,20,20,20,20]
const result = data.map((item) => {
return {
num: item.no,
total: test.filter(i => i === item.no).length // filters number array and then checks length
}
})
You can check next approach using a single for/of loop. But first I have to create a Set with valid ids, so I can discard noise data from the test array:
const data = [
{no: 3, name: 'drink'},
{no: 90, name: 'eat'},
{no: 20, name: 'swim'}
];
const userArr = [3,3,3,3,3,3,3,3,7,7,9,9,3,3,3,90,20,20,20,20];
let ids = new Set(data.map(x => x.no));
let newArr = [];
for (i of userArr)
{
let found = newArr.findIndex(x => x.num === i)
if (found >= 0)
newArr[found].total += 1;
else
ids.has(i) && newArr.push({num: i, total: 1});
}
console.log(newArr);

Getting values of nested objects and arrays - with plunker

I have two arrays, with nested objects, downloaded as part of calls to API endpoints, one (preview) has just numbers.
Example:
[{
obj1:[1, 2],
obj2:[3, 4]
}]
I had to make a second call to another endpoint, to get a list of IDs with strings
Example:
[{
obj1:[{
id:1,
name:'string_name1'
}, {
id:2,
name:'string_name2'
}]
}, {
obj2:[{
id:3,
name:'string_name3'
}, {
id:4,
name:'string_name4'
}]
}];
I need to match the IDs to the first array of objects numbers, so I have strings/text values to display on my web page
I have 2 functions
The first one, pulls the numbers from the preview array and pushes them to my own editable array that I will use to display on the page
This is the array before function runs
objName = [['obj1'], ['obj2']];
This is the first function, matches the names in preview to the names in my array and pushes values
setNumbers(){
for(let i = 0; i < this.objName.length; i++){
for(var name in this.preview[0]) {
if (name == this.objName[i][0]){
for(var val in this.preview[0][name]) {
this.objName[i].push(this.preview[0][name][val])
}
}
}
}
this.setStrings()
}
The second matches the IDs in fields to the numbers in objName and replaces with the string value
public setStrings(){
let feildId, feildName;
for(let i = 0; i < this.fields.length; i++){
var obj = this.fields[i]
for(var name in obj) {
if(this.objName[i][0] == name){
for(let j = 0; j < obj[name].length; j++){
feildId = obj[name][j].id
feildName = obj[name][j].name;
for(let x = 0; x < this.objName[i].length; x++){
if (this.objName[i][x] == feildId){
var index = this.objName[i].indexOf(feildId)
if (index !== -1) {
this.objName[i][index] = feildName;
}
}
}
}
}
}
}
console.log(this.objName)
}
The objName array, for output, ends up looking like:
[['obj1', 'string_name1', 'string_name2'], ['obj2', 'string_name3', 'string_name4']]
It works, but makes my eyes hurt, there must be an easier cleaner way of doing this?
Plunker link:
https://plnkr.co/edit/KBDu3ZehHl04er6eut6r?p=preview
Your data structures are not ideal for this kind of transformation. For instance, it would have been better if the display strings could be addressed directly given an "obj"-property and array index, without having to iterate through arrays.
Anyway, using the existing structure, you can still improve by using array functions, such as find and map:
class App {
constructor(preview, objName, fields) {
this.preview = preview;
this.objName = objName;
this.fields = fields;
this.setNumbers();
}
setNumbers() {
this.objName = this.objName.map( arr => arr.concat(this.preview[0][arr[0]]) );
this.setStrings();
}
setStrings() {
this.objName = this.objName.map( arr =>
[arr[0]].concat(arr.slice(1).map( val =>
this.fields.find( field => arr[0] in field )[arr[0]]
.find( item => item.id === val ).name
))
);
console.log(this.objName);
}
}
var objName = [['obj1'], ['obj2']],
preview = [{
obj1: [1, 2],
obj2: [3, 4]
}],
fields = [{
obj1:[{
id:1,
name:'string_name1'
}, {
id:2,
name:'string_name2'
}]
}, {
obj2:[{
id:3,
name:'string_name3'
}, {
id:4,
name:'string_name4'
}]
}];
new App(preview, objName, fields);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Note that this code assumes all searches lead to matches. If this is not your case, you'll have to add some code to define which values should be returned in case of non-matching references.
Here is such a variant of the code:
class App {
constructor(preview, objName, fields) {
this.preview = preview;
this.objName = objName;
this.fields = fields;
this.setNumbers();
}
setNumbers() {
this.objName = this.objName.map( arr =>
arr[0] in this.preview[0]
? arr.concat(this.preview[0][arr[0]])
: arr
);
this.setStrings();
}
setStrings() {
this.objName = this.objName.map( arr =>
[arr[0]].concat(arr.slice(1).map( val => {
let find = this.fields.find( field => arr[0] in field );
if (find) find = find[arr[0]].find( item => item.id === val );
return find ? find.name : val;
}))
);
console.log(this.objName);
}
}
var objName = [['obj1'], ['obj2'], ['obj3']],
preview = [{
obj1: [1, 2],
obj2: [3, 4, 5],
}],
fields = [{
obj1:[{
id:1,
name:'string_name1'
}, {
id:2,
name:'string_name2'
}]
}, {
obj2:[{
id:3,
name:'string_name3'
}, {
id:4,
name:'string_name4'
}]
}];
new App(preview, objName, fields);
.as-console-wrapper { max-height: 100% !important; top: 0; }
It's easier and cleaner to do this if you break it down into smaller pieces:
let objsToMap = [{
obj1: [1, 2, 7],
obj2: [3, 4],
obj3: [1, 2]
}]
let objValues = [{
obj1: [{
id: 1,
name: 'string_name1'
}, {
id: 2,
name: 'string_name2'
}]
}, {
obj2: [{
id: 3,
name: 'string_name3'
}, {
id: 4,
name: 'string_name4'
}]
}];
function findValueForId(objDef, id) {
let idKeyMap = objDef.find(item => item.id === id);
return idKeyMap ? idKeyMap.name : null;
}
function findObjectValues(valueMapping, key) {
let objectWithObjectValues = valueMapping.find(item => key in item);
return objectWithObjectValues ? objectWithObjectValues[key] : null;
}
// returns an array containing key followed by the values corresponding to the specified ids
function lookupObject(key, ids, valueMapping) {
let objDef = findObjectValues(valueMapping, key) || [];
let valuesForIds = ids.map(id => findValueForId(objDef, id));
let valuesWithoutBlanks = valuesForIds.filter(value => value);
return [key].concat(valuesWithoutBlanks);
}
let result = Object.entries(objsToMap[0]).map(([k, v]) => lookupObject(k, v, objValues));
console.log(result);
You'll notice that this approach uses .find() in two places because your second data structure nests everything into arrays instead of having direct property references. This isn't very good because it's not good for performance and makes the code more convoluted than it has to be.
Another option is to rearrange the second array before consuming it, so that it's like this:
let objValues = {
obj1: {
'1': 'string_name1',
'2': 'string_name2'
},
obj2: {
'3': 'string_name3',
'4': 'string_name4'
}
};
Here's how you could do that:
let objsToMap = [{
obj1: [1, 2, 7],
obj2: [3, 4],
obj3: [1, 2]
}]
let objValuesRaw = [{
obj1: [{
id: 1,
name: 'string_name1'
}, {
id: 2,
name: 'string_name2'
}]
}, {
obj2: [{
id: 3,
name: 'string_name3'
}, {
id: 4,
name: 'string_name4'
}]
}];
function cleanupObjDef(objDef) {
return objDef.reduce(function(acc, el) {
acc[el.id] = el.name;
return acc;
}, {});
}
function cleanupObjValues(objValues) {
let allCombined = Object.assign({}, ...objValues);
return Object.entries(allCombined).reduce(function (acc, [k, v]) {
acc[k] = cleanupObjDef(v);
return acc;
}, {});
}
// returns an array containing key followed by the values corresponding to the specified ids
function lookupObject(key, ids, valueMapping) {
let objDef = valueMapping[key] || {};
let valuesForIds = ids.map(id => objDef[id]);
let valuesWithoutBlanks = valuesForIds.filter(value => value);
return [key].concat(valuesWithoutBlanks);
}
let objValues = cleanupObjValues(objValuesRaw);
let result = Object.keys(objsToMap[0]).map(key => lookupObject(key, objsToMap[0][key], objValues));
console.log(result);

Categories