Iterate through nested loops and remove specific index that matches the condition - javascript

I have a temp array which originally looks something like this :
[{emp_id:1, acitivty:'run'}, {emp_id: 2, activity:'climb'}, {emp_id:1,activity:'swim'} .....]
Now, what I want is to merge the objects with the same emp_id. It should look like this:
[{emp_id:1, activity:'run', activity2:'swim'}, {emp_id:'2',activity:'climb'} .....]
Instead, I only got this, not showing the rest of the objects in the array:
[{emp_id:'1', activity:'run', activity2:'swim'}]
In my code, when the condition is met and the merging and splicing is done, I decrement the z because I think after splicing, the array will be reindexed and I think the array length will still be as is.
What seems to be the problem?
for(var z = 0; z < temp.length; z++) {
for(var x = 1; x < temp.length; x++) {
if(temp[z].emp_id == temp[x].emp_id) {
var ot_key = 'ot_time'+x+1;
var status_key = 'status'+x+1;
var dtr_key = 'dtr_out'+x+1;
Object.assign(temp[z], {
[ot_key] : temp[x].ot_time,
[status_key] : temp[x].status,
[dtr_key] : temp[x].dtr_out
})
temp.splice(x, 1);
z--;
}
}
}

Beside the spelling, you could take an object and store the wanted postfix in the objects. later take the payload.
For example have a look to the final object before getting the values and mapping only the payload property:
{
1: {
index: 3,
payload: { emp_id: 1, activity: "run", activity2: "swim" }
},
2: {
index: 2,
payload: { emp_id: 2, activity: "climb" }
}
}
Here emp_id is taken as key for grouping and because of the wanted structure an index is grouped as well for having a value for futher activities.
var data = [{ emp_id: 1, activity: 'run' }, { emp_id: 2, activity: 'climb' }, { emp_id: 1, activity: 'swim' }],
result = Object
.values(data.reduce((r, { emp_id, activity }) => {
if (r[emp_id]) r[emp_id].payload['activity' + r[emp_id].index++] = activity;
else r[emp_id] = { index: 2, payload: { emp_id, activity } };
return r;
}, {}))
.map(({ payload }) => payload);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
A better approach is to use an array for the activities.
var data = [{ emp_id: 1, activity: 'run' }, { emp_id: 2, activity: 'climb' }, { emp_id: 1, activity: 'swim' }],
result = Object.values(data.reduce((r, { emp_id, activity }) => {
if (r[emp_id]) r[emp_id].activity.push(activity);
else r[emp_id] = { emp_id, activity: [activity] };
return r;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

You can use array .reduce() method to get an array of activities grouped by emp_id which I think much cleaner.
const temp = [{emp_id:1,activity:"run"},{emp_id:2,activity:"climb"},{emp_id:1,activity:"swim"}];
const res = Object.values(temp.reduce((ac, { emp_id, activity }) => {
ac[emp_id] = ac[emp_id] || {emp_id, activity: []}
ac[emp_id].activity.push(activity)
return ac;
}, {}));
console.log(res);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Everything you can do in 2 loop, one for reduce another to collect output array.values.
Fast and better.
Note : If you dont want to use Object.keys, use 2nd solution
const data = [
{ emp_id: 1, activity: "run" },
{ emp_id: 2, activity: "climb" },
{ emp_id: 1, activity: "swim" }
];
const result = data.reduce((obj, { emp_id, activity }) => {
if (!obj[emp_id]) obj[emp_id] = { emp_id };
const count = Object.keys(obj[emp_id]).length;
obj[emp_id] = {
...obj[emp_id],
[`activity_${count}`]: activity
};
return obj;
}, {});
let finalResult = Object.values(result)
console.log(finalResult);
// If you dont want to use Object.keys
const result2 = data.reduce((obj, { emp_id, activity }) => {
if (!obj[emp_id]) obj[emp_id] = { count: 0, data: {} };
const count = obj[emp_id].count + 1;
obj[emp_id] = {
data: {
...obj[emp_id].data,
[`activity_${count}`]: activity
},
count: count
};
return obj;
}, {});
let finalResult2 = [];
for (let key in result2) {
const { data } = result2[key];
finalResult2.push(data);
}
console.log(finalResult2);
.as-console-wrapper { max-height: 100% !important; top: 0; color: blue!important; }

maybe like this
var data = [{
emp_id: 1, activity: 'run'
}, {
emp_id: 2, activity: 'climb'
}, {
emp_id: 1, activity: 'swim'
}];
var lookup = {};
var counter = {};
for (var i = 0; i < data.length; i++) {
var item = data[i];
if (lookup[item.emp_id]) {
var found = lookup[item.emp_id];
lookup[item.emp_id]['activity' + counter[item.emp_id]] = item.activity;
counter[item.emp_id]++;
data.splice(i, 1);
i--;
} else {
lookup[item.emp_id] = item;
counter[item.emp_id] = 2;
}
}
console.log(data);

You could use array.reduce() like this:
Be careful, your initial array has a spelling mistake in activity property.
let x =[
{emp_id:1, activity:'run'},
{emp_id: 2, activity:'climb'},
{emp_id:1,activity:'swim'}
];
let finalResult =
x.reduce((result, currentValue, currentIndex, arr) =>
{
return [...result,
arr.filter(f => f.emp_id === currentValue.emp_id &&
!result.find(r=>r.emp_id === f.emp_id)
)
.reduce((subResult, currentItem, index) =>
{
return {emp_id:currentItem.emp_id, ...subResult, ...{[`activity${index ==0 ? '' : '_' + (index+1)}`]:currentItem.activity}};
}, undefined)
];
}, [])
.filter(item=> !!item);
console.log(finalResult);

I would heed #NinaScholz's advice to use an array for activity values which will be much easier to handle afterwards when compared to testing for different related properties (and to fix the spelling of acitivty), but take a naive approach in creating a result array by using forEach(), which I find more readable. For example:
const source = [{emp_id: 1, activity: 'run'}, {emp_id: 2, activity: 'climb'}, {emp_id: 1, activity: 'swim'}];
let results = [];
// For each source item, check whether a result object with the current emp_id exists.
source.forEach(o => results.filter(r => r.emp_id === o.emp_id).length == 1
// Add activity to existing result object.
? results[o.emp_id].activity.push(o.activity)
// Create a new result object
: results.push({ emp_id: o.emp_id, activity: [o.activity] })
);
console.log(results);
.as-console-wrapper { max-height: 100% !important; top: 0; }

this solution will also take care if you have more then two
activity have same id
var x=[{emp_id:1, acitivty:'run'}, {emp_id: 2, activity:'climb'}, {emp_id:1,activity:'swim'},{emp_id:1,activity:'swim3'} ]
let myMap = new Map()
x.forEach(element =>{
let alreadyExist=myMap.has(element.emp_id)
if(alreadyExist){
let tempelement=myMap.get(element.emp_id)
tempelement.count=tempelement.count?++tempelement.count:2;
tempelement['acitivty'+tempelement.count]=element.activity
myMap.set(element.emp_id, tempelement)
}else{
myMap.set(element.emp_id, element)
}
});
//convert map to Array
let newArray=Array.from(myMap.values())
console.log(JSON.stringify(newArray))
//[{"emp_id":1,"acitivty":"run","count":3,"acitivty2":"swim","acitivty3":"swim3"},{"emp_id":2,"activity":"climb"}]

Related

JavaScript get unique array object data by 2 object unique

I have problem with find uniqueness by 2 value. I want do something like SQL GROUP BY Tag_No and PlatformID. I want find unique value by Tag_No and PlayformID where both value can't be duplicate
I have tried something like below, but it only works for one unique 'Tag_No'
var NewTag = [
{Tag_No:'xxx01',PlatformID:'12',Details:'example1'},
{Tag_No:'xxx02',PlatformID:'13',Details:'example2'},
{Tag_No:'xxx03',PlatformID:'14',Details:'example3'},
{Tag_No:'xxx05',PlatformID:'5',Details:'example4'},
{Tag_No:'xxx05',PlatformID:'12',Details:'example5'},
{Tag_No:'xxx05',PlatformID:'12',Details:'example6'},
]
var tmp = [];
var result = [];
if (NewTag !== [] /* any additional error checking */ ) {
for (var i = 0; i < NewTag.length; i++) {
var val = NewTag[i];
if (tmp[val.Tag_No] === undefined ) {
tmp[val.Tag_No] = true;
result.push(val);
}
}
}
console.log('result',result)
expected value is
result=[{Tag_No:'xxx01',PlatformID:'12',Details:'example1'},
{Tag_No:'xxx02',PlatformID:'13',Details:'example2'},
{Tag_No:'xxx03',PlatformID:'14',Details:'example3'},
{Tag_No:'xxx05',PlatformID:'5',Details:'example4'},
{Tag_No:'xxx05',PlatformID:'12',Details:'example5'},
]
use array.filter instead.
This filters your array on duplicates no matter what structure you have.
Reference
var NewTag = [
{Tag_No:'xxx01',PlatformID:'12',Details:'example'},
{Tag_No:'xxx02',PlatformID:'13',Details:'example'},
{Tag_No:'xxx03',PlatformID:'14',Details:'example'},
{Tag_No:'xxx05',PlatformID:'5',Details:'example'},
{Tag_No:'xxx05',PlatformID:'12',Details:'example'},
{Tag_No:'xxx05',PlatformID:'12',Details:'example'},
]
const uniqueArray = NewTag.filter((value, index) => {
const _value = JSON.stringify(value);
return index === NewTag.findIndex(obj => {
return JSON.stringify(obj) === _value;
});
});
console.log('result',uniqueArray)
You can use hash grouping approach:
const data = [{Tag_No:'xxx01',PlatformID:'12',Details:'example'},{Tag_No:'xxx02',PlatformID:'13',Details:'example'},{Tag_No:'xxx03',PlatformID:'14',Details:'example'},{Tag_No:'xxx05',PlatformID:'5',Details:'example'},{Tag_No:'xxx05',PlatformID:'12',Details:'example'},{Tag_No:'xxx05',PlatformID:'12',Details:'example'}];
const result = Object.values(data.reduce((acc, item) => {
const hash = [item.Tag_No, item.PlatformID].join('---');
acc[hash] ??= item;
return acc;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0 }
Here is my solution:
let NewTag = [
{Tag_No:'xxx01',PlatformID:'12',Details:'example'},
{Tag_No:'xxx02',PlatformID:'13',Details:'example'},
{Tag_No:'xxx03',PlatformID:'14',Details:'example'},
{Tag_No:'xxx05',PlatformID:'5',Details:'example'},
{Tag_No:'xxx05',PlatformID:'12',Details:'example'},
{Tag_No:'xxx05',PlatformID:'12',Details:'example'},
]
let temp=[]
let result=[];
NewTag.forEach(tag=>{
let key=tag.Tag_No+"\t"+tag.PlatformID;
if (!temp.includes(key)){
temp.push(key);
result.push(tag)
}
});
console.log(result)
You could use Set to check for uniqueness
const NewTag = [
{ Tag_No: "xxx01", PlatformID: "12", Details: "example" },
{ Tag_No: "xxx02", PlatformID: "13", Details: "example" },
{ Tag_No: "xxx03", PlatformID: "14", Details: "example" },
{ Tag_No: "xxx05", PlatformID: "5", Details: "example" },
{ Tag_No: "xxx05", PlatformID: "12", Details: "example" },
{ Tag_No: "xxx05", PlatformID: "12", Details: "example" },
]
const uniquePairSet = new Set()
const res = NewTag.reduce((acc, el) => {
const Tag_No_PlatformID = `${el.Tag_No}-${el.PlatformID}`
if (!uniquePairSet.has(Tag_No_PlatformID)) {
uniquePairSet.add(Tag_No_PlatformID)
acc.push(el)
}
return acc
}, [])
console.log("result", res)
References
Set

How do I go about creating a javascript object that stores the count of unique objects?

Lets say I have an array
['Student', 'Progress', 'Student', 'Area']
I would like to have an object that looks like this...
{
{
value: "Student",
count: 2
},
{
value: "Progress",
count: 1
}
}
The code I am using currently is this... which clearly is wrong. Any help would be appreciated.
const counts = [];
for (let i = 0; i < array.length; i++) {
var object = {
value: counts[array[i]],
count: (1 + (counts[array[i]] || 0))
}
counts.push(object);
};
return counts;
You could take an object for counting and map the entries of the object as new object.
const
data = ['Student', 'Progress', 'Student', 'Area'],
result = Object
.entries(data.reduce((r, s) => {
r[s] = (r[s] || 0) + 1;
return r;
}, {}))
.map(([value, count]) => ({ value, count }));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Array of objects how do i check for deeply nested text string duplicates & remove from array?

I have an array of objects
Deep inside those objects is a text string
I want to check if other objects in the same array have the same text string / are duplicates.
Then i need a new array with those duplicates removed.
I thought this would be quite simple but it's been testing my intellect for two days now.
const arr = [
{..obj 1}
{..obj 2}
{..obj 3}
{
id: 4,
uid: 24872-2847-249249892842,
tags: ['some', 'stuff'],
type: "blogpage",
href: "https://link-to-stuff",
first_publication_date: "2020-02-12T16:05:04+0000",
last_publication_date: "2020-02-18T21:52:06+0000",
data: {
...some stuff
heading: [
{ type: "heading1", text: "Here Is My Text I Need To Check Duplicates
Of"}
]
}
}
{..obj 5}
{..obj 6}
{..obj 7}
{..obj 8}
{..obj 9}
{..obj 10}
]
I figured something like:
filterOutDuplicates = (blogIndexContent) => {
let arr = blogIndexContent.pages;
let results = [];
arr.map(each => {
if (!results || !results.length) {
results.push(each);
} else {
for (let i = 0; i < results.length; i++) {
const headline = results[i].data.heading[0].text;
if (headline === each.data.heading[0].text) {
return;
} else {
return results.push(each);
}
}
}
})
console.log('Results :', results); // <-- this just gives me the same 9 blog stories again, no duplicates removed.
}
What am i doing wrong guys?
If you dont mind using lodash, it could be easily solved using _.uniqBy
const withoutDups = _.uniqBy(arr, 'data.heading[0].text')
Try this
const arr = [
{
id: 4,
data: {
heading: [
{
type: "heading1",
text: "Here Is My Text I Need To Check Duplicates Of"
}
]
}
},
{
id: 5,
data: {
heading: [
{
type: "heading1",
text: "Here Is My Text I Need To Check Duplicates Of"
}
]
}
},
{
id: 6,
data: {
heading: [
{
type: "heading1",
text: "Not Duplicates"
}
]
}
}
];
const withoutDuplicates = arr.reduce(
(prev, curr) =>
prev
.map(d => d["data"]["heading"][0]["text"])
.includes(curr["data"]["heading"][0]["text"])
? [curr]
: [...prev, curr],
[]
);
console.log(withoutDuplicates);
Slight changes to your code
1) remove using map, have loop over array.
2) Build the uniq object with keys. (Here headline is what we want)
3) Add to results array only when key is not in uniq
let arr = blogIndexContent.pages;
let results = [];
const uniq = {};
for (let i = 0; i < arr.length; i++) {
const headline = arr[i].data.heading[0].text;
if (!(headline in uniq)) {
results.push(each);
uniq[each] = 1;
}
}
console.log("Results :", results);
This should work for you:
filterOutDuplicates = blogIndexContent => {
let arr = blogIndexContent.pages
const result = []
arr.forEach(each => {
if (result.length === 0) {
result.push(each)
}
else {
const headline = each.data.heading[0].text
let found = false
for (let i = 0; i < result.length; i++) {
if (result[i].data.heading[0].text === headline) {
found = true
break
}
}
if (!found) {
result.push(each)
}
}
})
console.log('Results :', results)
}

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);
});

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