I'm struggling with Javascript Object manipulation to output the desired result - javascript

I have an array of objects that I'd like to group into particular keys and format.
Here is the Object I have:
{
"id": "98765",
"things": [{
"clientId": "123456",
"val": "file1",
"cmpId": "54353"
},
{
"clientId": "1234",
"val": "file2",
"cmpId": "3453"
},
{
"clientId": "1234",
"val": "file3",
"cmpId": "5433"
}
]
};
My aim is to try and get the Object into the following format
{
"id":"98765",
"things":{
"123456":{
"54353":{
"val":"file1"
}
},
"1234":{
"3453":{
"val":"file2"
},
"5433":{
"val":"file3"
}
}
}
}
I have managed to achieve getting the 'clientId' to be a key of the Objects, but am struggling to get the campaign IDs to be nested within that Object.
This is the code I've tried
const a = obj.things.reduce((ac, {clientId, ...rest})=> (ac[clientId] = rest, ac), {})
console.log(a);
This gives a format of the following which obviously doesn't achieve the cmpId nesting, but also gets rid of one of my Objects as the clientId is a duplicate. I'm a bit lost about how I achieve something quite neat without horrible reams of code to achieve it.
{
'1234': { val: 'file3', cmpId: '5433' },
'123456': { val: 'file1', cmpId: '54353' }
}

You were almost there, you also need to get the cmpId and then check if there is already an object for clientId, here is an example:
const input = {
"id": "98765",
"things": [{
"clientId": "123456",
"val": "file1",
"cmpId": "54353"
},
{
"clientId": "1234",
"val": "file2",
"cmpId": "3453"
},
{
"clientId": "1234",
"val": "file3",
"cmpId": "5433"
}
]
};
input.things = input.things.reduce((a, {
clientId,
cmpId,
...rest
}) => {
if (a[clientId]) {
a[clientId][cmpId] = rest;
} else {
a[clientId] = {
[cmpId]: rest
};
}
return a;
}, {});
console.log(input);

You're close. Using Array#reduce is the right approach. You just need to change ac[clientId] = rest to the structure you want:
const obj = {
"id": "98765",
"things": [{
"clientId": "123456",
"val": "file1",
"cmpId": "54353"
},
{
"clientId": "1234",
"val": "file2",
"cmpId": "3453"
},
{
"clientId": "1234",
"val": "file3",
"cmpId": "5433"
}
]
};
obj.things = obj.things.reduce( ( res, { clientId, cmpId, ...rest } ) => {
res[clientId] = { ...res[clientId], [cmpId]: rest };
return res;
}, { } );
console.log( obj );

You could use .reduce() to map/combine the things property, like so:
const data = { "id": "98765", "things": [{ "clientId": "123456", "val": "file1", "cmpId": "54353" }, { "clientId": "1234", "val": "file2", "cmpId": "3453" }, { "clientId": "1234", "val": "file3", "cmpId": "5433" } ] };
const newData = { id: data.id,
things: data.things.reduce((out, {clientId, cmpId, ...rest}) => //for each "thing"
({...out, [clientId]: {...out[clientId], [cmpId]: rest}}) //Create/merge with existing clientId
, {})
};
console.log(newData);

This version is similar to several others here. It's designed to avoid mutating your original structure and the allow additional properties at both levels to be passed through to the output, caring only about things, clientId, and cmpId.
const transform = ({things, ...rest}) => ({
...rest,
things: things.reduce(
(a, {clientId, cmpId, ...rest}) => ({...a, [clientId]: {...a[clientId], [cmpId]: rest}}),
{}
)
})
const data = {id: "98765", things: [{clientId: "123456", val: "file1", cmpId: "54353"}, {clientId: "1234", val: "file2", cmpId: "3453"}, {clientId: "1234", val: "file3", cmpId: "5433"}]}
console .log (
transform (data)
)

try using JSONPath incase you are aimimg to get your desired result by iterating over the the object.It is an excellent tool which basically coverts the whole json into XML

Related

Need to filter an JavaScript object

I need to filter an JS object as below format. I have some array of Object. every object contain title and data. I need to filter the object. title will not repet in object it will make an array of data and store every data in that.
Object
let results = [
{
"title": "exam",
"data": {
"status": "Active",
"studentId": "44",
"universityId": "0",
"mediaId": "12",
"mediaName": "massey university",
"mediaSrc": "https://unisearch-static-contents.s3.ap-southeast-1.amazonaws.com/ADMIN/massey%20university-9a2b9a22-b648-4ef1-a806-97bb0da9b337.jpg",
"mediaDownload": 4
}
},
{
"title": "prospectus",
"data": {
"status": "Active",
"studentId": "44",
"universityId": "0",
"mediaId": "12",
"mediaName": "massey university",
"mediaSrc": "https://unisearch-static-contents.s3.ap-southeast-1.amazonaws.com/ADMIN/massey%20university-9a2b9a22-b648-4ef1-a806-97bb0da9b337.jpg",
"mediaDownload": 4
}
},
{
"title": "prospectus",
"data": {
"status": "Active",
"studentId": "44",
"universityId": "23",
"mediaId": "12",
"mediaName": "massey university",
"mediaSrc": "https://unisearch-static-contents.s3.ap-southeast-1.amazonaws.com/ADMIN/massey%20university-9a2b9a22-b648-4ef1-a806-97bb0da9b337.jpg",
"mediaDownload": 4
}
},]
Filter object will be like this. title will not be repeated it will make an array.
"results": [
{
"title": "exam",
"data": {
"status": "Active",
"studentId": "44",
"universityId": "0",
"mediaId": "12",
"mediaName": "massey university",
"mediaSrc": "https://unisearch-static-contents.s3.ap-southeast-1.amazonaws.com/ADMIN/massey%20university-9a2b9a22-b648-4ef1-a806-97bb0da9b337.jpg",
"mediaDownload": 4
}
},
{
"title": "prospectus",
"data": [{
"status": "Active",
"studentId": "44",
"universityId": "0",
"mediaId": "12",
"mediaName": "massey university",
"mediaSrc": "https://unisearch-static-contents.s3.ap-southeast-1.amazonaws.com/ADMIN/massey%20university-9a2b9a22-b648-4ef1-a806-97bb0da9b337.jpg",
"mediaDownload": 4
},
{
"status": "Active",
"studentId": "44",
"universityId": "23",
"mediaId": "12",
"mediaName": "massey university",
"mediaSrc": "https://unisearch-static-contents.s3.ap-southeast-1.amazonaws.com/ADMIN/massey%20university-9a2b9a22-b648-4ef1-a806-97bb0da9b337.jpg",
"mediaDownload": 4
}]
I have tried this.
let filterData = [];
mainData.data.results.map((value) => {
if (filterData.length == 0) {
let obj = {
title: "title",
data: [{ ...value.data }],
};
filterData.push(obj);
} else {
let found = false;
}
});
This isn't really a filter operation, more of a reduce job where you want to collect data for the same title.
For that, I'd recommend creating a map of title to data
// this is just your data structure minified
const results = [{"title":"exam","data":{"status":"Active","studentId":"44","universityId":"0","mediaId":"12","mediaName":"massey university","mediaSrc":"https://unisearch-static-contents.s3.ap-southeast-1.amazonaws.com/ADMIN/massey%20university-9a2b9a22-b648-4ef1-a806-97bb0da9b337.jpg","mediaDownload":4}},{"title":"prospectus","data":{"status":"Active","studentId":"44","universityId":"0","mediaId":"12","mediaName":"massey university","mediaSrc":"https://unisearch-static-contents.s3.ap-southeast-1.amazonaws.com/ADMIN/massey%20university-9a2b9a22-b648-4ef1-a806-97bb0da9b337.jpg","mediaDownload":4}},{"title":"prospectus","data":{"status":"Active","studentId":"44","universityId":"23","mediaId":"12","mediaName":"massey university","mediaSrc":"https://unisearch-static-contents.s3.ap-southeast-1.amazonaws.com/ADMIN/massey%20university-9a2b9a22-b648-4ef1-a806-97bb0da9b337.jpg","mediaDownload":4}}];
// Collect data by title
const collect = results.reduce(
(map, { title, data }) => ({
...map,
[title]: [...(map[title] ?? []), data],
}),
{}
);
// Convert the mapped object to an array of objects
// with `title` and `data` properties
const filterData = Object.entries(collect).map(([title, data]) => ({
title,
data,
}));
console.log(filterData);
.as-console-wrapper { max-height: 100% !important; }
This will always put data in an array which keeps your data-structure consistent. If you really want single-element arrays to be destructured, use this instead for the last part
const filterData = Object.entries(collect).map(([title, data]) => ({
title,
data: data.length === 1 ? data[0] : data,
}));
The main problem here is to remember which title already exists and reduce it to just one with the data of all the same titles.
The above solution is good but it has a complexity of O(n^2) because it performs two loops. One to reduce the elements into an array of { title: [datas]} objects and one to transform it into the desired output of the form [{title, datas},...].
Using this solution we can perform the process with a complexity of O(n), using a hash table (directory) structure to remember the titles and reduce only those that are repeated.
let filterData = []
let directory = {}
mainData.data.results.map( (obj) => {
if(directory[obj.title] === undefined) {
directory[obj.title] = filterData.length
filterData.push({title: obj.title, data: [obj.data] })
} else {
filterData[directory[obj.title]].data.push(obj.data)
}
})
var resultObj = {}
results.forEach((item)=>{
if(!resultObj[item.title]){
resultObj[item.title] = {
title:item.title,
data:[]
}
}
resultObj[item.title].data.push(item.data)
})
var finalResultList = Object.values(resultObj)
console.log(finalResultList)

How to generate a nested array of objects from an source json object

Using the value from an array of object I'm trying to generate an another array of objects with a different structure than that of the original array.
In the following example arrays you can see that from the key:value pairs the key is changing between the Source array and Result array but the values are same. I had tried some code but not worthwhile to share. What logic or method can be used in this case to generate the object structure mentioned below
Source array
[
{
"node": {
"Charecter": "Hulk",
"Publisher": "Marvel",
"Movie": "Avengers"
}
},
{
"node": {
"Charecter": "Wolverine",
"Publisher": "Marvel",
"Movie": "X-Men"
}
},
{
"node": {
"Charecter": "Superman",
"Publisher": "DC",
"Movie": "Man of steel"
}
}
]
Result array
[
{
"Franchise": "Marvel",
"Data": [
{
"Lead": "Hulk",
"In": "Avengers"
},
{
"Lead": "Wolverine",
"In": "X-Men"
}
]
},
{
"Franchise": "DC",
"Data": [
{
"Lead": "Superman",
"In": "Man of steer"
}
]
},
]
Using .reduce() and .find() combination you can achieve the goal.
Try the following:
const data = [{ "node": { "Charecter": "Hulk", "Publisher": "Marvel", "Movie": "Avengers" } }, { "node": { "Charecter": "Wolverine","Publisher": "Marvel","Movie": "X-Men" } }, {"node": { "Charecter": "Superman", "Publisher": "DC","Movie": "Man of steel" } }];
const result = data.reduce((a, c) => {
const found = a.find(e => e.Franchise === c.node.Publisher);
if (found) found.Data.push({ Lead: c.node.Charecter, In: c.node.Movie });
else a.push({ Franchise: c.node.Publisher, Data: [{ Lead: c.node.Charecter, In: c.node.Movie }] });
return a;
}, []);
console.log(result)
I hope this helps!
You can do it with Array#reduce method where group everything within an object and extract object values using Object.values method.
const data = [{"node":{"Charecter":"Hulk","Publisher":"Marvel","Movie":"Avengers"}},{"node":{"Charecter":"Wolverine","Publisher":"Marvel","Movie":"X-Men"}},{"node":{"Charecter":"Superman","Publisher":"DC","Movie":"Man of steel"}}]"
const result = Object.values(data.reduce((obj, { node: { Publisher: p, ...rest }}) => {
// define key if not defined
obj[p] = obj[p] || { Franchise: p, Data: [] };
// push to the array
obj[p].Data.push(rest);
return obj;
}, {}));
console.log(result)

How can i change the organization of my json?

Hey i tried to transform a json file (as a js object but I can not do it). Here is an example of my problem:
Input object
{
"peoples": [
{
"name": "Alain",
"nationality": "Italian"
},
{
"name": "John",
"nationality": "French"
},
{
"name": "FOO",
"nationality": "French"
}
]
}
Output object
{
"nationality": {
"french": {
"peoples": [{ "name": "John" }, { "name": "FOO" }]
},
"italian": {
"peoples": [{ "name": "Alain" }]
}
}
}
How can i do this ? maybe Lodash but i have not find any way to do this. Can anyone help me ?
You can use a simple reduce:
const obj = {
"peoples": [{
"name": "Alain",
"nationality": "Italian"
},
{
"name": "John",
"nationality": "French"
},
{
"name": "FOO",
"nationality": "French"
}
]
}
const output = obj.peoples.reduce((a, {nationality: n, ...rest}) => {
const x = a.nationality[n]
if (x) x.push(rest)
else a.nationality[n] = [rest]
return a
}, { nationality: {} })
console.log(output)
Note, I've used a spread operator to get the rest of the properties, so if you were to add more properties to each person, then those would be included in the new object.

How to flatten an array inside of an object?

Basically, I want to flatten an array inside of an object with Javascript (preferably ES6). I'm actually not sure if this is even an issue of flattening, I just want a good way to make this change.
I want to go from this:
{
id: "123",
name: "test",
history: [
{
id: "456",
name: "test2"
},
{
id: "789",
name: "test3"
}
]
}
To this...
{
id: "123",
name: "test"
},
{
id: "456",
name: "test2"
},
{
id: "789",
name: "test3"
}
Essentially in the original object, I have a "history" property that related to that specific object. Any ideas?
You can use destructuring and rest syntax, to separate history and the 1st object, and then combine them to a single array with spread or concat.
const { history, ...obj1 } = {"id":"123","name":"test","history":[{"id":"456","name":"test2"},{"id":"789","name":"test3"}]}
const result = [obj1, ...history]
console.log(result)
try this:
const data = {
"id": "123",
"name": "test",
"history": [
{
"id": "456",
"name": "test2"
},
{
"id": "789",
"name": "test3"
}
]
}
const {id, name, history} = data ;
const result = [{id, name} , ...history];
console.log(result);

Change structure of a JavaScript object

I have this object structure:
"users": {
"1": {
"id": 1,
"name": "John",
"email": "john#doe.com",
"supplier_id": 1,
"supplier_name": [
"Supplier1"
],
"supplier_code": "SUP001",
"count": "21"
}
}
I'd like to change it so it appears like this:
"users": {
"1": {
"id": 1,
"name": "John",
"email": "john#doe.com",
"suppliers":[
{
"supplier_id": 1,
"supplier_name": [
"Supplier1"
]
}
],
"supplier_code": "SUP001",
"count": "21"
}
}
I tried this hoping it would work:
const group = accumulator[item.id];
group.suppliers = [];
group.suppliers = group.suppliers.push(item.supplier_name, item.supplier_id, item.supplier_code);
return accumulator;
Unfortunately that just seems to give me a count of the objects pushed into suppliers, suppliers isn't an array and supplier_id, supplier_name and supplier_code are still visible outside of suppliers:
"users": {
"1": {
"id": 1,
"name": "John",
"email": "john#doe.com",
"supplier_id": 1,
"supplier_name": [
"Supplier1"
],
"supplier_code": "SUP001",
"count": "21",
"suppliers: 3
}
}
How do I change it to the format I want?
You could use es6 Destructuring assignment, Object.values es2017 (or Object.keys instead).
If you assume that users contains more then one user you could use reduce.
In the example below original object won't be mutated.
Hope it helps
const original = {
"users": {
"1": {
"id": 1,
"name": "John",
"email": "john#doe.com",
"supplier_id": 1,
"supplier_name": [
"Supplier1"
],
"supplier_code": "SUP001",
"count": "21"
}
}
};
const { users } = original;
const reshaped = Object.values(users).reduce((acc, { id, supplier_id, supplier_name, ...rest }) => {
acc[id] = {
...rest,
suppliers: [{
supplier_id,
supplier_name: [supplier_name]
}]
};
return acc;
}, {});
console.log(reshaped);
You need to use an object to push into the suppliers array. Also, delete the old keys which are not needed.
Edit - You can directly create an array of 1 object. Thanks #Adam
const group = accumulator[item.id];
group.suppliers = [{
supplier_id: item.supplier_id,
supplier_name: item.supplier_name,
supplier_code: item.supplier_code
}];
delete group.supplier_id;
delete group.supplier_name;
delete group.supplier_code;
return accumulator;
Here's a quick and modern solution:
const parseUsers = (users) => {
let parsedUsers = {};
for (key in users) {
const user = users[key];
// destructuring (or extracting) the relevant keys from the . user object, keeping everything else under 'rest'
const { supplier_id, supplier_name, ...rest } = user;
parsedUsers[key] = {
...rest, // spreading back our rest params
suppliers: [ // creating a new array and populating it with the keys which we previously extracted (along with their corresponding values)
supplier_id,
supplier_name
]
}
}
return parsedUsers;
}
usage: parseUsers(json.users)

Categories