Here is a guide on how to transform the data:
const info = [{
"title": "Event 1",
"sponsors": [
{"name": "Woolworths","location": "Melbourne"},
{"name": "Coles","location": "Melbourne"},
{"name": "Metricon","location": "Wagga Wagga"}
]
},
{
"title": "Event 2",
"sponsors": [
{"name": "Metricon","location": "Albert Park"},
{"name": "Woolworths","location": "Melbourne"},
{"name": "ALDI","location": "Bendigo"}
]
},
{
"title": "Event 3",
"sponsors": [
{"name": "Holcim","location": "Bendigo"},
{"name": "Westpac","location": "Melbourne"},
{"name": "Coles","location": "Mildura"}
]
}
];
const byLocation = info.reduce((result, event) => (event.sponsors.forEach(sponsor => {
if (!result[sponsor.location]) result[sponsor.location] = {};
if (!result[sponsor.location][sponsor.name]) result[sponsor.location][sponsor.name] = [];
result[sponsor.location][sponsor.name].push(event.title);
}), result), {});
console.log(byLocation);
To clarify the above:
To convert the initial info
Array of events
[
event,
event,
// ...
]
into an Object - where all the property names (locations) are distinct
{
"location name 1": {},
"location name 2": {},
// ...
}
the first approach would be to utilize Array.prototype.reduce()
MDN:
const byLocation = info.reduce((finalObject, event) => {
// Add event details into finalObject here
return finalObject; // Return the new Object for the next iteration
}, {}); // << {} represents the finalObject
a bit of renaming and using Arrow function's Implicit Return we can simplify the above as follows:
const byLocation = info.reduce((r, e) => (/*Insert e details into r*/, r), {});
Therefore, e
signifies the current Event being processed, and r
is the Resulting object we need to construct.
Let's fill up the r
object by looping through the e.sponsors
Array
e.sponsors.forEach(sp => { // sp is the active Sponsor
// If r["location name"] doesn't exist, create an empty object for it
if (!r[sp.location]) r[sp.location] = {};
// If r["location name"]["Sponsor name"] doesn't exist, create it
// as an empty array
if (!r[sp.location][sp.name]) r[sp.location][sp.name] = [];
// Finally, add the Event title into that array.
r[sp.location][sp.name].push(e.title);
})
If the concept seems too complex, another way to write it could look like this:
const info = [{
"title": "Event 1",
"sponsors": [
{"name": "Woolworths","location": "Melbourne"},
{"name": "Coles","location": "Melbourne"},
{"name": "Metricon","location": "Wagga Wagga"}
]
},
{
"title": "Event 2",
"sponsors": [
{"name": "Metricon","location": "Albert Park"},
{"name": "Woolworths","location": "Melbourne"},
{"name": "ALDI","location": "Bendigo"}
]
},
{
"title": "Event 3",
"sponsors": [
{"name": "Holcim","location": "Bendigo"},
{"name": "Westpac","location": "Melbourne"},
{"name": "Coles","location": "Mildura"}
]
}
];
const byLocation = {}; // The resulting object with unique Locations
info.forEach((event) => {
const sponsors = event.sponsors;
sponsors.forEach((sponsor) => {
if (!byLocation.hasOwnProperty(sponsor.location)) {
byLocation[sponsor.location] = {};
}
if (!byLocation[sponsor.location].hasOwnProperty(sponsor.name)) {
byLocation[sponsor.location][sponsor.name] = [];
}
byLocation[sponsor.location][sponsor.name].push(event.title);
});
});
console.log(byLocation);
To showcase the data within a <ul id="list"></ul>
, in JavaScript:
const html = Object.entries(byLocation).reduce((h, [location, ob]) => {
h += `<li>${location}<ul>`;
Object.entries(ob).forEach(([sponsor, events]) => {
h += `<li>${sponsor}
<ul>
<li>${events.join('</li><li>')}</li>
</ul>
</li>`;
});
h += `</ul></li>`;
return h
}, '');
document.querySelector('#list').insertAdjacentHTML('beforeend', html);