Related
How to combine JSON objects in the same response that has the same key and value with javascript? This is my data for example:
{
"data": [
{
"name": "A",
"description": {
"location": "location1",
"floor": "floor1",
},
},
{
"name": "A",
"description": {
"location": "location2",
"floor": "floor1",
},
},
{
"name": "B",
"description": {
"location": "location3",
"floor": "floor3",
},
},
]
}
And turn it into this:
{
"data": [
{
"name": "A",
"description": {
"location": ["location1","location2"],
"floor": "floor1",
},
},
{
"name": "B",
"description": {
"location": "location3",
"floor": "floor3",
},
},
]
}
Basically I am someone who is new to learning javascript. Any help would be very helpful, thank you.
You can do:
const data = {data: [{name: 'A',description: {location: 'location1',floor: 'floor1',},},{name: 'A',description: {location: 'location2',floor: 'floor1',},},{name: 'B',description: {location: 'location3',floor: 'floor3',},},],}
const result = {
data: data.data.reduce((a, { name, description }) => {
const index = a.findIndex((d) => d.name === name)
if (index >= 0) {
let location = a[index].description.location
location = Array.isArray(location) ? location : [location]
a[index].description.location = [...location, description.location]
} else {
a.push({ name, description })
}
return a
}, []),
}
console.log(result)
const list = {
"data": [
{
"name": "A",
"description": {
"location": "location1",
"floor": "floor1",
},
},
{
"name": "A",
"description": {
"location": "location2",
"floor": "floor1",
},
},
{
"name": "B",
"description": {
"location": "location3",
"floor": "floor3",
},
},
]
};
const consolidatedData = [];
for (const ele of list.data) {
const isExist = consolidatedData.find(x => x.name === ele.name);
if (!isExist) {
consolidatedData.push({
...ele
})
} else {
const objectKey = consolidatedData.findIndex(x => x.name === ele.name);
if (objectKey > -1) {
const description = consolidatedData[objectKey].description;
const newDes = ele.description;
if (newDes.location !== description.location) {
const data = consolidatedData[objectKey].description;
const added = [data.location, ele.description.location];
delete consolidatedData[objectKey].description.location
consolidatedData[objectKey].description["location"] = added
}
if (newDes.floor !== description.floor){
const data = consolidatedData[objectKey].floor;
const added = [data.floor, ele.description.floor];
delete consolidatedData[objectKey].description.floor
consolidatedData[objectKey].description["floor"] = added
}
}
}
}
console.log(JSON.stringify(consolidatedData, null, 2));
Here is a solution that uses an intermediate bucket object. The desired result object is then constructed from the bucket object:
const input = { "data": [ { "name": "A", "description": { "location": "location1", "floor": "floor1", }, }, { "name": "A", "description": { "location": "location2", "floor": "floor1", }, }, { "name": "B", "description": { "location": "location3", "floor": "floor3", }, }, ] };
let buckets = input.data.reduce((acc, obj) => {
if(!acc[obj.name]) {
acc[obj.name] = {
locations: {},
floors: {}
};
}
acc[obj.name].locations[obj.description.location] = true;
acc[obj.name].floors[obj.description.floor] = true;
return acc;
}, {});
console.log('buckets: ', buckets);
let result = {
data: Object.keys(buckets).map(name => {
let locations = Object.keys(buckets[name].locations);
let floors = Object.keys(buckets[name].floors);
return {
name: name,
description: {
location: locations.length == 1 ? locations[0] : locations,
floor: floors.length == 1 ? floors[0] : floors
}
}
})
};
console.log('result:', result);
Notes:
buckets object:
is created using an array .reduce()
array .reduce() docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
locations and floors are collected using objects instead of arrays, this is to avoid duplicate names
result object:
is using Object.keys(buckets) to get the array of names
.map() transforms each name into the desired object
your unusual array or string value for location and floor is constructed with a conditional
I have absolutely no idea of which title I could write.
Actually, here is what I get from API :
[
{
"order": 1,
"role": {
"label": "singer"
},
"artist": {
"name": "AaRON"
}
},
{
"order": 1,
"role": {
"label": "author"
},
"artist": {
"name": "Simon Buret"
}
},
{
"order": 2,
"role": {
"label": "author"
},
"artist": {
"name": "Olivier Coursier"
}
},
{
"order": 1,
"role": {
"label": "composer"
},
"artist": {
"name": "John Doe"
}
}
]
And here is what I need to send :
"artist": {
"singer": [
"AaRON"
],
"author": [
"Simon Buret",
"Olivier Coursier"
]
}
Of course, the order property must be taken in account.
Example : Simon Buret is the first item because he has the order set to 1.
I have absolutely no idea how to implement that, I just did a map, but don't know what to put inside :/
this.artistControl.controls.map(artistControl => {
...
});
Is there a way to do what I need ?
Does this work for you:
let arr = [
{ "order": 1, "role": { "label": "singer" }, "artist": { "name": "AaRON" } },
{ "order": 1, "role": { "label": "author" }, "artist": { "name": "Simon Buret" } },
{ "order": 2, "role": { "label": "author" }, "artist": { "name": "Olivier Coursier" } },
{ "order": 1, "role": { "label": "composer" }, "artist": { "name": "John Doe" } }
];
let obj = {'artist': {}};
arr.forEach(a => {
obj['artist'][a.role.label] = obj['artist'][a.role.label] || [];
obj['artist'][a.role.label][a.order-1] = a.artist.name;
});
console.log(obj);
You could use reduce method with object as a accumulator param and then check if the key doesn't exist create it with empty array as value and then add names by order.
const data = [{"order":1,"role":{"label":"singer"},"artist":{"name":"AaRON"}},{"order":1,"role":{"label":"author"},"artist":{"name":"Simon Buret"}},{"order":2,"role":{"label":"author"},"artist":{"name":"Olivier Coursier"}},{"order":1,"role":{"label":"composer"},"artist":{"name":"John Doe"}}]
const result = data.reduce((r, {
role: { label },
artist: { name },
order
}) => {
if (name) {
if (!r[label]) r[label] = [];
r[label][order - 1] = name;
}
return r;
}, {})
console.log(result)
const array = [{"order":1,"role":{"label":"singer"},"artist":{"name":"AaRON"}},{"order":1,"role":{"label":"author"},"artist":{"name":"Simon Buret"}},{"order":2,"role":{"label":"author"},"artist":{"name":"Olivier Coursier"}},{"order":1,"role":{"label":"composer"},"artist":{"name":"John Doe"}}];
const result = array
.sort((item1, item2) => item1.order - item2.order)
.reduce((acc, { role, artist }) => ({
...acc,
artist: {
...acc.artist,
[role.label]: [
...(acc.artist[role.label] || []),
artist.name,
],
},
}), { artist: {} });
console.log(result);
Here is another approach with es5
const data = [{ "order": 1, "role": { "label": "singer" }, "artist": { "name": "AaRON" } }, { "order": 1, "role": { "label": "author" }, "artist": { "name": "Simon Buret" } }, { "order": 2, "role": { "label": "author" }, "artist": { "name": "Olivier Coursier" } }, { "order": 1, "role": { "label": "composer" }, "artist": { "name": "John Doe" } }];
var result = data.reduce(function(map, obj) {
map["artist"] = map["artist"] || {};
if (obj.role.label === 'author' || obj.role.label === 'singer') {
map["artist"][obj.role.label] = map["artist"][obj.role.label] || [];
map["artist"][obj.role.label][obj.order - 1] = obj.artist.name;
}
return map;
}, {});
console.log(result)
This is what I have: I want to merge object which key begins with "path-"+i . And to strip "path-i" from keys in end result.
var arr = [
{
"key": "path-0-mp4",
"value": [
"media/video/01.mp4",
"media/video/01_hd.mp4"
]
},
{
"key": "path-0-quality",
"value": [
"720p",
"1080p"
]
},
{
"key": "path-1-mp4",
"value": [
"media/video/02.mp4",
"media/video/02_hd.mp4"
]
},
{
"key": "path-1-quality",
"value": [
"SD",
"HD"
]
}
]
This is a desired result:
var arr = [
[
{
"mp4": "media/video/01.mp4",
"quality": "720p"
},
{
"mp4": "media/video/01_hd.mp4",
"quality": "1080p"
},
],
[
{
"mp4": "media/video/02.mp4",
"quality": "SD"
},
{
"mp4": "media/video/02_hd.mp4",
"quality": "HD"
},
],
]
I started doing something but its not even close:
var key, new_key, value,j=0, z=0, parr = [], obj;
for(var i = 0;i<a.length;i++){
console.log('item:' ,a[i])
key = a[i].key, value = a[i].value
if(key.indexOf('path-'+j.toString()) > -1){
new_key = key.substr(key.lastIndexOf('-')+1)
console.log(key, new_key, value)
for(var z = 0;z<value.length;z++){
parr.push({[new_key]: value[z] })
}
}
}
console.log(parr)
[
{
"mp4": "media/video/01.mp4"
},
{
"mp4": "media/video/01_hd.mp4"
},
{
"quality": "720p"
},
{
"quality": "1080p"
}
]
edit:
Array could petencially hols different keys that would need grouping in the same way, for example:
var arr = [
{
"key": "path-0-mp4",
"value": [
"media/video/01.mp4",
"media/video/01_hd.mp4"
]
},
{
"key": "path-0-quality",
"value": [
"720p",
"1080p"
]
},
{
"key": "path-1-mp4",
"value": [
"media/video/02.mp4",
"media/video/02_hd.mp4"
]
},
{
"key": "path-1-quality",
"value": [
"SD",
"HD"
]
},
{
"key": "subtitle-0-label",
"value": [
"English",
"German",
"Spanish"
]
},
{
"key": "subtitle-0-src",
"value": [
"data/subtitles/sintel-en.vtt",
"data/subtitles/sintel-de.vtt",
"data/subtitles/sintel-es.vtt"
]
},
{
"key": "subtitle-1-label",
"value": [
"German",
"Spanish"
]
},
{
"key": "subtitle-1-src",
"value": [
"data/subtitles/tumblr-de.vtt",
"data/subtitles/tumblr-es.vtt"
]
}
]
This is a desired result (create new array for each different key):
var arr = [
[
{
"mp4": "media/video/01.mp4",
"quality": "720p"
},
{
"mp4": "media/video/01_hd.mp4",
"quality": "1080p"
},
],
[
{
"mp4": "media/video/02.mp4",
"quality": "SD"
},
{
"mp4": "media/video/02_hd.mp4",
"quality": "HD"
},
],
],
arr2 = [
[
{
"label": "English",
"src": "data/subtitles/sintel-en.vtt",
},
{
"label": "German",
"src": "data/subtitles/sintel-de.vtt"
},
{
"label": "Spanish",
"src": "data/subtitles/sintel-es.vtt"
}
],
[
{
"label": "Spanish",
"src": "data/subtitles/tumblr-es.vtt",
},
{
"label": "German",
"src": "data/subtitles/tumblr-de.vtt"
}
]
]
You could split the key property, omit the first path and take the rest as index and key. Then create a new array, if not exists and assign the values.
var data = [{ key: "path-0-mp4", value: ["media/video/01.mp4", "media/video/01_hd.mp4"] }, { key: "path-0-quality", value: ["720p", "1080p"] }, { key: "path-1-mp4", value: ["media/video/02.mp4", "media/video/02_hd.mp4"] }, { key: "path-1-quality", value: ["SD", "HD"] }],
result = data.reduce((r, { key, value }) => {
let [, i, k] = key.split('-');
r[i] = r[i] || [];
value.forEach((v, j) => (r[i][j] = r[i][j] || {})[k] = v);
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you like to group by the first part of key, you could take an object with this group as key and assign the rest as above.
var data = [{ key: "path-0-mp4", value: ["media/video/01.mp4", "media/video/01_hd.mp4"] }, { key: "path-0-quality", value: ["720p", "1080p"] }, { key: "path-1-mp4", value: ["media/video/02.mp4", "media/video/02_hd.mp4"] }, { key: "path-1-quality", value: ["SD", "HD"] }, { key: "subtitle-0-label", value: ["English", "German", "Spanish"] }, { key: "subtitle-0-src", value: ["data/subtitles/sintel-en.vtt", "data/subtitles/sintel-de.vtt", "data/subtitles/sintel-es.vtt"] }, { key: "subtitle-1-label", value: ["German", "Spanish"] }, { key: "subtitle-1-src", value: ["data/subtitles/tumblr-de.vtt", "data/subtitles/tumblr-es.vtt"] }],
result = data.reduce((r, { key, value }) => {
let [group, i, k] = key.split('-');
if (!r[group]) r[group] = [];
if (!r[group][i]) r[group][i] = [];
value.forEach((v, j) => {
if (!r[group][i][j]) r[group][i][j] = {};
r[group][i][j][k] = v;
});
return r;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I am new to this and a beginner,
is this the correct approach?
const a = [{ "key": "path-0-mp4", "value": [ "media/video/01.mp4", "media/video/01_hd.mp4" ] }, { "key": "path-0-quality", "value": [ "720p", "1080p" ] }, { "key": "path-1-mp4", "value": [ "media/video/02.mp4", "media/video/02_hd.mp4" ] }, { "key": "path-1-quality", "value": [ "SD", "HD" ] } ];
var resp = [];
for (let i = 0; i < a.length; i++) {
var inst = a[i];
var key = inst["key"];
for (let j = 0; j < inst.value.length; j++) {
var index = key.split("-")[1];
var keyinst = key.split("-")[2];
if (!resp[index]) {
resp[index] = [];
}
if (!resp[index][j]) {
resp[index][j] = {};
}
resp[index][j][keyinst] = inst.value[j];
}
}
console.log(resp);
I find this easier to read and grasp
You can save an assignment if you use reduce
const arr = [{ "key": "path-0-mp4", "value": [ "media/video/01.mp4", "media/video/01_hd.mp4" ] }, { "key": "path-0-quality", "value": [ "720p", "1080p" ] }, { "key": "path-1-mp4", "value": [ "media/video/02.mp4", "media/video/02_hd.mp4" ] }, { "key": "path-1-quality", "value": [ "SD", "HD" ] } ];
newArr = [];
arr.filter(item => item.key.endsWith("mp4"))
.forEach(item => item.value
.forEach((val, i) => newArr.push({
"mp4": val,
"quality": arr.find(qItem => qItem.key === item.key.replace("mp4", "quality")).value[i]}
)
)
)
console.log(newArr)
Here is Nina's version in an unobfuscated version
var data = [{ key: "path-0-mp4", value: ["media/video/01.mp4", "media/video/01_hd.mp4"] }, { key: "path-0-quality", value: ["720p", "1080p"] }, { key: "path-1-mp4", value: ["media/video/02.mp4", "media/video/02_hd.mp4"] }, { key: "path-1-quality", value: ["SD", "HD"] }],
result = data.reduce((resultArray, { key, value }) => {
let [, idx, suffix] = key.split('-');
resultArray[idx] = resultArray[idx] || [];
value.forEach((val, i) => (resultArray[idx][i] = resultArray[idx][i] || {})[suffix] = val);
return resultArray;
}, []);
console.log(result);
The only odd thing I did here was using an object as a lookup table to help with the speed complexity. If you have any questions let me know.
const arr = [{ "key": "path-0-mp4", "value": [ "media/video/01.mp4", "media/video/01_hd.mp4" ] }, { "key": "path-0-quality", "value": [ "720p", "1080p" ] }, { "key": "path-1-mp4", "value": [ "media/video/02.mp4", "media/video/02_hd.mp4" ] }, { "key": "path-1-quality", "value": [ "SD", "HD" ] } ];
const result = arr.reduce((table, item) => {
// Getting "path-1" from "path-1-quality"
const pathValues = item.key.split('-');
const pathValue = pathValues[0] + '-' + pathValues[1];
// Getting "quality" from "path-1-quality"
const key = pathValues[2];
// Get Index from table if already registered paths
let tIndex = table.indexLookup[pathValue];
// If there is no registered index register one
if (tIndex === undefined) {
// reassign index to new location
tIndex = table.result.length;
// register the index
table.indexLookup[pathValue] = tIndex;
table.result.push([]);
}
// Assign values
item.value.forEach((value, i) => {
const arr = table.result[tIndex] || [];
arr[i] = arr[i] || {}
arr[i][key] = value;
table.result[tIndex] = arr;
})
return table
}, {
indexLookup : {},
result: []
}).result
console.log(result)
So in the API response example below, focusing on env_variables, I am trying grab the value for secret. I am stuck because as you can see, the name and value are not nested together. I am not familiar with how to grab the value based on the name in this example.
api response:
{
"id": 1146,
"job": {
"name": "jobname1",
},
"env_variables": [
{
"name": {
"name": "test1"
},
"value": {
"value": "10.13.6"
}
},
{
"name": {
"name": "test1"
},
"value": {
"value": "10.13.6"
}
},
],
},
{
"id": 1147,
"job": {
"name": "jobname2",
},
"env_variables": [
{
"name": {
"name": "secret"
},
"value": {
"value": "10.13.7"
}
},
{
"name": {
"name": "test5"
},
"value": {
"value": "10.13.6"
}
},
],
}
js
jobs: []
apiEndpoint = "test.com/api"
fetch(this.apiEndpoint)
.then(response => response.json())
.then(body => {
for(let i=0; i<body.length; i++){
this.jobs.push({
'build_id': JSON.stringify(body[i].id),
'secret': //not sure how to pull the value (10.13.7)
})
}
})
You need nested loops, since there are two nested arrays: the top level of the response is an array of objects, and env_variables contains an array of objects.
fetch(this.apiEndpoint)
.then(response => response.json())
.then(body => {
for (let i = 0; i < body.length; i++) {
let env = body[i].env_variables;
for (let j = 0; j < env.length; j++) {
if (env[j].name.name == "secret") {
this.jobs.push({
'build_id': JSON.stringify(body[i].id),
'secret': env[j].value.value
})
}
}
}
})
You can do something like this inside .then(body=>...
const body = [{ //it looks like brackets [] were lost in OP
"id": 1146,
"job": {
"name": "jobname1",
},
"env_variables": [{
"name": {
"name": "test1"
},
"value": {
"value": "10.13.6"
}
},
{
"name": {
"name": "test1"
},
"value": {
"value": "10.13.6"
}
},
],
},
{
"id": 1147,
"job": {
"name": "jobname2",
},
"env_variables": [{
"name": {
"name": "secret"
},
"value": {
"value": "10.13.7"
}
},
{
"name": {
"name": "test5"
},
"value": {
"value": "10.13.6"
}
},
],
}
];
let secret = null;
body.forEach(b => {
let el = b.env_variables.find(e => e.name.name == 'secret');
if (el) { //found
secret = el.value.value;
return false; //exit forEach
}
});
console.log(secret);
You could also do something like this with Array.forEach and Array.find:
let data = [{ "id": 1146, "job": { "name": "jobname1", }, "env_variables": [{ "name": { "name": "test1" }, "value": { "value": "10.13.6" } }, { "name": { "name": "test1" }, "value": { "value": "10.13.6" } }, ], }, { "id": 1147, "job": { "name": "jobname2", }, "env_variables": [{ "name": { "name": "secret" }, "value": { "value": "10.13.7" } }, { "name": { "name": "test5" }, "value": { "value": "10.13.6" } }, ], } ]
let jobs = []
data.forEach(({id, env_variables}) => jobs.push({
build_id: id,
secret: ((env_variables.find(({name}) =>
name.name === 'secret') || {}).value || {}).value || 'N/A'
// ... other props
}))
console.log(jobs)
Assuming your result is an array, you could do something like this:
let secrets = results.reduce((result, item) => {
let secret = item["env_variables"].find((v) => {return v.name.name === "secret"})
if(secret){
result.push({id:item.id, secret: secret.value.value});
}
return result;
}, []);
This would return an array of objects like {id: 1, secret: ""} for each object in your result set that has a secret.
If you don't care whether the secret is present or not, you could modify the code slightly like this:
let secrets = results.reduce((result, item) => {
let secret = item["env_variables"].find((v) => {return v.name.name === "secret"})
result.push({id:item.id, secret: secret ? secret.value.value : ""});
return result;
}, []);
Which just leaves with you an empty string on the levels where there is no secret.
I'm facing some issue in for loop while creating an object from array of object.I have an array as this in node js app:
[
{
"Material": "113/133",
"Name": [
{
"name": "WELD1",
"value": 27520
},
{
"name": "WELD2",
"value": 676992
},
{
"name": "WELD3",
"value": 421
}
]
},
{
"Material": "150/300",
"Name": [
{
"name": "WELD1",
"value": 1441
},
{
"name": "WELD2",
"value": 555
},
{
"name": "WELD3",
"value": 100992
}
]
}
]
I want to return object like this which contains all the Material as array, Name and there value in array of object like this:
{
Material: ["113/133", "150/300"],
datasets: [
{
label: "WELD1",
data: [27520,1441]
},
{
label: "WELD2",
data: [676992,555]
},
{
label: "WELD3",
data: [100,20,0]
}
]
}
I want to get result using for loop.
you can use .reduce() and do something like this:
var arr = [
{
"Material": "113/133",
"Name": [
{
"name": "WELD1",
"value": 27520
},
{
"name": "WELD2",
"value": 676992
},
{
"name": "WELD3",
"value": 421
}
]
},
{
"Material": "150/300",
"Name": [
{
"name": "WELD1",
"value": 1441
},
{
"name": "WELD2",
"value": 555
},
{
"name": "WELD3",
"value": 100992
}
]
}
];
var newArr = arr.reduce((acc, ob) => {
for (var key in ob)
if(typeof acc[key] === 'object')
acc[key] = acc[key] ? acc[key].concat(ob[key]) : [ob[key]];
else
acc[key] ? acc[key].push(ob[key]) : acc[key] = [ob[key]];
return acc;
}, {});
console.log(newArr);
let array = [
{
"Material": "113/133",
"Name": [
{
"name": "WELD1",
"value": 27520
},
{
"name": "WELD2",
"value": 676992
},
{
"name": "WELD3",
"value": 421
}
]
},
{
"Material": "150/300",
"Name": [
{
"name": "WELD1",
"value": 1441
},
{
"name": "WELD2",
"value": 555
},
{
"name": "WELD3",
"value": 100992
}
]
}
]
let answer = {Material: [], datasets: []}
array.forEach(x => {
answer.Material.push(x.Material);
x.Name.forEach(na => {
let object = answer.datasets.find(obj => obj.label === na.name) || {label: "", data: []};
if(object.label === ""){
object.label = na.name;
object.data.push(na.value);
answer.datasets.push(object);
}else{
object.data.push(na.value)
}
});
});
console.log(answer);
The above is alternative solution using forEach instead of reduce
Use of Array.reduce to build your new data structure using data you have
const start = [{
"Material": "113/133",
"Name": [{
"name": "WELD1",
"value": 27520
},
{
"name": "WELD2",
"value": 676992
},
{
"name": "WELD3",
"value": 421
}
]
},
{
"Material": "150/300",
"Name": [{
"name": "WELD1",
"value": 1441
},
{
"name": "WELD2",
"value": 555
},
{
"name": "WELD3",
"value": 100992
}
]
}
];
const end = start.reduce((tmp, {
Material,
Name,
}) => {
// Handle the material
// If it do not exist in the array, push it
if (!tmp.Material.includes(Material)) {
tmp.Material.push(Material);
}
// Handle the datasets
// Look at each Name
Name.forEach(({
name,
value,
}) => {
// Can we find the label?
const labelFind = tmp.datasets.find(y => y.label === name);
// If we can't find the label, create a new dataset
if (!labelFind) {
tmp.datasets.push({
label: name,
data: [
value,
],
});
return;
}
// If we has found it push new value in the dataset
labelFind.data.push(value);
});
return tmp;
}, {
Material: [],
datasets: [],
});
console.log(end);
// This is the old fashioned way.
// Iterate over whole array,
// make a map, push value where 'name' is found in map
// later iterate over this map - dataMap - and form required datasets array.
var Material = [];
var dataMap = {};
arr.forEach(obj => {
Material.push(obj.Material);
obj.Name.forEach(item => {
if(dataMap[item.name]){
dataMap[item.name].push(item.value);
}
else {
dataMap[item.name] = [item.value];
}
});
});
var datasets = [];
Object.keys(dataMap).forEach(label => {
datasets.push({
label: label,
data: dataMap[label]
});
});
var result = {
Material: Material,
datasets: datasets
}
console.log(result);