Remove nested array from an object - javascript

What is the best way to convert
const mockResults = [
[{ user: { firstName: '1', lastName: '1' }, status: 'WRONG' }],
[{ user: { firstName: '2',lastName: '2' }, status: 'WRONG' }],
[{ user: { firstName: '3',lastName: '3' }, status: 'CORRECT' }]
];
to
const mockResults = [
{ user: { firstName: '1', lastName: '1' }, status: 'WRONG' },
{ user: { firstName: '2',lastName: '2' }, status: 'WRONG' },
{ user: { firstName: '3',lastName: '3' }, status: 'CORRECT' }
];
The whole task is to transform mockResults to requiredFormat, that's why I need to remove nested arrays:
const requiredFormat = [
{
status: 'WRONG',
data: [{ user: {firstName: '1', lastName: '1'}}, { user: {firstName: '2', lastName: '2'}}],
},
{
status: 'CORRECT',
data: [{ user: {firstName: '3', lastName: '3'}}],
},
];
Here's what I tried so far:
https://jsfiddle.net/9uLje3sg/
Thanks!

You can use flat method from Array javascript object. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat
If you want to change the source of data and change the shape of it, using map and reduce methods can help you.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
In your precise example reduce would fit as you are creating a new object grouping per status property.
const mockResults = [
[{ user: { firstName: '1', lastName: '1' }, status: 'WRONG' }],
[{ user: { firstName: '2',lastName: '2' }, status: 'WRONG' }],
[{ user: { firstName: '3',lastName: '3' }, status: 'CORRECT' }]
];
const flattedAndReduced = mockResults.flat().reduce( (acc, curr)=>{
const statusIndex = { 'WRONG' : 0, 'CORRECT': 1 };
acc[statusIndex[curr.status]].data.push({ user: curr.user} );
return acc;
}, [
{
status: 'WRONG',
data: [],
},
{
status: 'CORRECT',
data: [],
}
]
);
console.log(flattedAndReduced);

Use the function map as follow which returns an array with the desired objects.
let result = mockResults.map(([user]) => user);
That approach is assuming there is only one index per array from the original array.
According to the approach for requiredFormat
You can use the function reduce for grouping and the function Object.values for getting the desired output.
const mockResults = [
[{ user: { firstName: '1', lastName: '1' }, status: 'WRONG' }],
[{ user: { firstName: '2',lastName: '2' }, status: 'WRONG' }],
[{ user: { firstName: '3',lastName: '3' }, status: 'CORRECT' }]
];
let requiredFormat = Object.values(mockResults.reduce((a, [{user, status}]) => {
(a[status] || (a[status] = {data: [], status})).data.push(user);
return a;
}, Object.create(null)));
console.log(requiredFormat);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Simply use Array.prototype.map() to return the object from first index.
Please Note: variable declared with const can not be modified, use *let instead.
let mockResults = [
[{ user: { firstName: '1', lastName: '1' }, status: 'WRONG' }],
[{ user: { firstName: '2',lastName: '2' }, status: 'WRONG' }],
[{ user: { firstName: '3',lastName: '3' }, status: 'CORRECT' }]
];
mockResults = mockResults.map(i => i[0]);
console.log(mockResults);

const mockResults = [
[{ user: { firstName: '1', lastName: '1' }, status: 'WRONG' }],
[{ user: { firstName: '2',lastName: '2' }, status: 'WRONG' }],
[{ user: { firstName: '3',lastName: '3' }, status: 'CORRECT' }]
];
const requiredFormat = [
{status: 'WRONG', data: []},
{status: 'CORRECT', data: []},
];
for(let [{user,status}] of mockResults) {
requiredFormat[ status==="WRONG" ? 0 : 1].data.push({user});
}
console.log(requiredFormat);

Related

Convert an array of object to a new array of object with keys in javscript

I have an array of objects in the format below and would like to transform it into a new array of objects using a property as a key. The key should be unique. See shape of the object below
const mockedList = [
{
email: 'aaa#example.com',
id: '5052',
name: 'Java',
},
{
email: 'bbb#example.com',
id: '5053',
name: 'Python',
},
{
email: 'aaa#example.com',
id: '5054',
name: 'C#',
},
{
email: 'bbb#example.com',
id: '5055',
name: 'Javascript',
},
];
I would like to transform this and get an array of objects with keys and values in this format.
[
{
email: 'bbb#example.com',
languages: [
{
email: 'bbb#example.com',
id: '5055',
name: 'Javascript',
},
{
email: 'bbb#example.com',
id: '5053',
name: 'Python',
},
]
},
{
email: 'aaa#example.com',
languages: [
{
email: 'aaa#example.com',
id: '5052',
name: 'Java',
},
{
email: 'aaa#example.com',
id: '5054',
name: 'C#',
},
]
}
]
I've tried using map-reduce
const result = mockedList.reduce((r, a) => {
r[a.email] = r[a.email] || [];
r[a.email].push(a);
return r;
}, Object.create(null));
But did not get the right shape of data
You can do:
const mockedList = [{email: 'aaa#example.com',id: '5052',name: 'Java',},{email: 'bbb#example.com',id: '5053',name: 'Python',},{email: 'aaa#example.com',id: '5054',name: 'C#',},{ email: 'bbb#example.com', id: '5055', name: 'Javascript' },]
const mockedListHash = mockedList.reduce((a, c) => {
a[c.email] = a[c.email] || { email: c.email, languages: [] }
a[c.email].languages.push(c)
return a
}, {})
const result = Object.values(mockedListHash)
console.log(result)
In case you want to clean the repeated emails within languages:
const mockedList = [{email: 'aaa#example.com',id: '5052',name: 'Java',},{email: 'bbb#example.com',id: '5053',name: 'Python',},{email: 'aaa#example.com',id: '5054',name: 'C#',},{ email: 'bbb#example.com', id: '5055', name: 'Javascript' },]
const mockedListHash = mockedList.reduce((a, c) => {
a[c.email] = a[c.email] || { email: c.email, languages: [] }
a[c.email].languages.push({
id: c.id,
name: c.name,
})
return a
}, {})
const result = Object.values(mockedListHash)
console.log(result)
Here is another option with simple for loop
// Array
const mockedList = [
{
email: 'aaa#example.com',
id: '5052',
name: 'Java'
},
{
email: 'bbb#example.com',
id: '5053',
name: 'Python'
},
{
email: 'aaa#example.com',
id: '5054',
name: 'C#'
},
{
email: 'bbb#example.com',
id: '5055',
name: 'Javascript'
}
];
// Set new object
const newObj = {};
// Use regular loop
for(const el of mockedList) {
// Use email as key
// If key already exist, add info
// to it's languages array
if(newObj[el.email]) newObj[el.email].languages.push(el);
else newObj[el.email] = {
email: el.email,
languages: [el]
}
}
// Test
console.log(newObj);
// If you need just array of objects,
// without email as key, then transform it
const newArr = Object.keys(newObj).map((key) => newObj[key]);
// Test
console.log(newArr);

How to filter an array of objects, based on some fields?

I have an array of objects like this:
const myArray = [
{
id: 1234,
name: 'foo',
status: 'OK'
},
{
id: 1235,
name: 'foo',
status: 'KO'
},
{
id: 1236,
name: 'bar',
status: 'KO'
},
{
id: 1237,
name: 'bar',
status: 'OK'
},
{
id: 1238,
name: 'baz',
status: 'KO'
}
]
and I need to filter it, keeping only one with the same name, and it should be the one with the highest id.
const expectedOutput = [
{
id: 1235,
name: 'foo',
status: 'KO'
},
{
id: 1237,
name: 'bar',
status: 'OK'
},
{
id: 1238,
name: 'baz',
status: 'KO'
}
]
I've been strugling but I can't find the best solution. Any idea?
Keep track of maxes in an object mapping names to objects:
const myArray = [
{
id: 1234,
name: 'foo',
status: 'OK'
},
{
id: 1235,
name: 'foo',
status: 'KO'
},
{
id: 1236,
name: 'bar',
status: 'KO'
},
{
id: 1237,
name: 'bar',
status: 'OK'
},
{
id: 1238,
name: 'baz',
status: 'KO'
}
];
const maxes = {};
for (const ele of myArray) {
if (!(ele.name in maxes) || ele.id > maxes[ele.name].id) {
maxes[ele.name] = ele;
}
}
const filtered = Object.values(maxes);
console.log(filtered);
.as-console-wrapper {min-height: 100%;}
You can use reduce like the following. This way it will work for both sorted and unsorted array.
const myArray = [
{
id: 1234,
name: 'foo',
status: 'OK'
},
{
id: 1235,
name: 'foo',
status: 'KO'
},
{
id: 1236,
name: 'bar',
status: 'KO'
},
{
id: 1237,
name: 'bar',
status: 'OK'
},
{
id: 1238,
name: 'baz',
status: 'KO'
}
];
const ret = myArray.reduce((acc, curr) => {
const index = acc.findIndex(item => item.name === curr.name);
if(index> -1 && acc[index].id < curr.id) {
acc[index] = curr;
} else {
acc.push(curr);
}
return acc;
}, []);
console.log(ret);
Although this will work pretty good as you have to loop through the array only once. But if you use for loop instead of reduce. It will be much faster as for loops are usually faster than map, filter, reduce etc. You can do the following for fastest result,
const myArray = [
{
id: 1234,
name: 'foo',
status: 'OK'
},
{
id: 1235,
name: 'foo',
status: 'KO'
},
{
id: 1236,
name: 'bar',
status: 'KO'
},
{
id: 1237,
name: 'bar',
status: 'OK'
},
{
id: 1238,
name: 'baz',
status: 'KO'
}
];
let ret = [];
for(let i =0;i<myArray.length; i++) {
const index = ret.findIndex(item => item.name === myArray[i].name);
if(index > -1 && ret[index].id < myArray[i].id) {
ret[index]=myArray[i];
} else {
ret.push(myArray[i]);
}
}
console.log(ret);
Since the array is already sorted by id you could use a Map object and just set each value using the name as key. Overriding the previous value if present. Note that this only matches the requirements as long as the last element with a certain name has also the highest value.
const myArray = [{id:1234,name:'foo',status:'OK'},{id:1235,name:'foo',status:'KO'},{id:1236,name:'bar',status:'KO'},{id:1237,name:'bar',status:'OK'},{id:1238,name:'baz',status:'KO'}];
const lookup = new Map();
myArray.forEach(item => lookup.set(item.name, item));
const result = Array.from(lookup.values());
console.log(result);
The order of the resulting elements is based on insertion order into the Map object. The first key inserted will be the first element of the resulting array. The second key inserted will be the second element, etc.
You could do it using Map Object.
First, create a new Map Object
Traverse the array using forEach() method.
Put name as a key into a variable named key
Check if key exists by using has(key) method in the Map Object named map
If key does not exist then set it into the Map Object by calling the set(key, value) method. In this solution, key is name and value is object.
If Key exists then get the object using get(key) method, get max id using Math.max() method, then update the object and set it into the Map Object.
const myArray = [
{
id: 1234,
name: 'foo',
status: 'OK',
},
{
id: 1235,
name: 'foo',
status: 'KO',
},
{
id: 1236,
name: 'bar',
status: 'KO',
},
{
id: 1237,
name: 'bar',
status: 'OK',
},
{
id: 1238,
name: 'baz',
status: 'KO',
},
];
const map = new Map();
myArray.forEach((x) => {
const key = x.name;
if (map.has(key))
map.set(key, { ...map.get(key), id: Math.max(map.get(key).id, x.id) });
else map.set(key, { ...x });
});
const ret = [...map.values()];
console.log(ret);

How would you change values/add to nested object data inside array of objects using Javascript?

const beers = [
{
id: '100',
name: 'stoneys'
},
{
id: '200',
name: 'budweiser'
},
{
id: '300',
name: 'miller'
},
{
id: '400',
name: 'corona'
}
];
const people = [
{
name: 'steve',
teams: [
{
name: 'pirates',
beers: ['100']
},
{
name: 'penguins',
beers: ['300']
}
]
},
{
name: 'jim',
teams: [
{
name: 'indians',
beers: ['200']
},
{
name: 'blue jackets',
beers: ['100', '400']
}
]
}
];
let newPeople = people.map(fan => {
fan.teams.map(team => {
team.beers.map(beer => beers.filter(brand => brand.id === beer)[0])
});
});
Above is a sample I put together to best demonstrate my question. I am having trouble understanding why nested mapping (.map()) of object arrays is not allowing me to alter the nested data. When I console log results, I am either getting an "[undefined, undefined]' or the unchanged "people" array.
I would like to return the same array as "people" except replace the nested "beers" array (people.teams.beers[]) with corresponding objects from the "beers" array. Example of a successful result below:
{
name: 'steve',
teams: [
{
name: 'pirates',
beers: [
{
id: '100',
name: 'stoneys'
}
]
},
{
name: 'penguins',
beers: [
{
id: '300',
name: 'miller'
}
]
}
]
}
Array.map expects a function which takes single array element as parameter and returns a mapped value. In your case you're not returning any value from mapping functions therefore you're getting undefined twice
const beers = [
{
id: '100',
name: 'stoneys'
},
{
id: '200',
name: 'budweiser'
},
{
id: '300',
name: 'miller'
},
{
id: '400',
name: 'corona'
}
];
const people = [
{
name: 'steve',
teams: [
{
name: 'pirates',
beers: ['100']
},
{
name: 'penguins',
beers: ['300']
}
]
},
{
name: 'jim',
teams: [
{
name: 'indians',
beers: ['200']
},
{
name: 'blue jackets',
beers: ['100', '400']
}
]
}
];
let newPeople = people.map(fan => {
let teams = fan.teams.map(team => {
let beer = team.beers.map(beer => beers.filter(brand => brand.id === beer)[0]);
return { name: team.name, beers: beer }
});
return { name: fan.name, teams: teams }
});
console.log(newPeople);

How to loop save array of objects to mongodb

I want to store this data to mongodb, But I don't know how to loop it, Tried for a long time and couldn't get the correct answer
There is tree data
[
{
name: 'A',
children: [
{
name: 'A1',
children: [
{
name: 'A11',
children: []
}
]
},
{
name: 'A2',
children: []
},
]
},
{
name: 'B',
children: [
{
name: 'B1',
children: []
},
{
name: 'B2',
children: [
{
name: 'B21',
children: []
}
]
},
]
},
]
There is my Schema
const TempSchema = new mongoose.Schema({
name: String,
parent: { type: ObjectId, ref: 'Temp' },
}
I hope to get this result
{ _id: 5dea0671855f5d4b44774afd, name: 'A', parent: null, },
{ _id: 5dea07383ef7973e80883efd, name: 'A1', parent: 5dea0671855f5d4b44774afd, },
{ _id: 5dea07461047036d7c958771, name: 'A11', parent: 5dea07383ef7973e80883efd, },
{ _id: 5def00c05de2b22f8e6b9bfe, name: 'A2', parent: 5dea0671855f5d4b44774afd, },
...
This problem has troubled me for a long time, hope to get everyone's help, thank you!
Create ID and flat
const ObjectId = mongoose.Types.ObjectId;
const flatten = (data, parent = null) =>
data.flatMap(e => {
e._id = ObjectId();
return ([{
_id: e._id,
parent,
name: e.name
},
...flatten(e.children, e._id)])
});
const result = flatten(tree);
await Model.insertMany(result)

Map array inside an object

I've the following complex JSON object that has got array and array inside an array. How can parse those arrays and create an object array for each element in the array. I'm using lodash library in my project, just in case if there are any functions available.
{
studentId: 123
name: XYZ
phone: 34234234
gender: M
subjects: [{
studentId: 123
subjectName: Math
scores:50
assignments:[
{
type: Internal,
submitted: yes,
status: failed
},
{
type: External,
submitted: yes,
status: passed
}]
},
{
studentId: 123
subjectName: Science
score: 20
assignments:[
{
type: Internal,
submitted: yes,
status: passed
},
{
type: External,
submitted: yes,
status: failed
}]
}]
}
Expecting:
[{
studentId:123,
name: XYZ
phone: 34234234
gender: M,
subjectName: Math
scores:50
assignments:[
{
type: Internal,
submitted: yes,
status: failed
},
{
type: External,
submitted: yes,
status: passed
}]
},
{
studentId:123,
name: XYZ
phone: 34234234
gender: M,
subjectName: science
scores:20
assignments:[
{
type: Internal,
submitted: yes,
status: failed
},
{
type: External,
submitted: yes,
status: passed
}]
}
]
You can use omit to get the details of the student without the subjects array, use these details to transform each item in the subjects array using defaults through map.
var details = _.omit(data, 'subjects');
var result = _.map(data.subjects, function(subject) {
return _.defaults({}, details, subject);
});
var data = {
studentId: '123',
name: 'XYZ',
phone: '34234234',
gender: 'M',
subjects: [{
studentId: '123',
subjectName: 'Math',
scores: 50,
assignments: [{
type: 'Internal',
submitted: 'yes',
status: 'failed'
},
{
type: 'External',
submitted: 'yes',
status: 'passed'
}
]
},
{
studentId: '123',
subjectName: 'Science',
score: 20,
assignments: [{
type: 'Internal',
submitted: 'yes',
status: 'passed'
},
{
type: 'External',
submitted: 'yes',
status: 'failed'
}
]
}
]
};
var details = _.omit(data, 'subjects');
var result = _.map(data.subjects, function(subject) {
return _.defaults({}, details, subject);
});
console.log(result);
body > div { min-height: 100%; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
I encourage you to use Normalizr package. It's very useful and takes all care about your collections even if they're nested.

Categories