-
Notifications
You must be signed in to change notification settings - Fork 0
3.4 Data transformation for stacked bar chart
I started out by mapping over the original array with objects i fetched from the endpoint, and returned only values that were neccessary for my chart like so:
export default function filterData(data){
//map over data objects and make new array filled with modified objects
const objectsArray = data
.map(object => {
return{
origin: object.herkomstSuperLabel.value,
type: object.typeLabel.value,
amount: parseInt(object.amount.value)
}
})
console.log("objectsarray: ", objectsArray)
return objectsArray
}I wanted the data to be transformed in this manner:
So i had to get rid of some keys and merge these with values form other key value pairs. I knew that the best way to do thought the best way to do this was to flatten each object and filter the keys for type and amount out:
const destructuredArray = objectsArray.map(element => {
// resource object.entries: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
let destructure = Object.entries(element);
//flatten each array in object
let flatArrays = destructure.flat();
//filter out type and amount keys, resource used: https://flaviocopes.com/how-to-remove-item-from-array/
let modifiedArrays = flatArrays.filter( item => item !== 'type' && item !== 'amount');
console.log('modd', modifiedArrays)
return modifiedArrays
});destructuredArray now looked like this:
0: (4) ["origin", "Amerika", "opiumpijpen", 2]
1: (4) ["origin", "Afrika", "tabakspijpen", 51]
2: (4) ["origin", "Azië", "waterpijpen", 103]
3: (4) ["origin", "Afrika", "pijpen (rookgerei)", 321]
4: (4) ["origin", "Amerika", "hasjpijpen", 1]
5: (4) ["origin", "Eurazië", "tabakspijpen", 2]
6: (4) ["origin", "Oceanië", "tabakspijpen", 39]
7: (4) ["origin", "Afrika", "waterpijpen", 2]
8: (4) ["origin", "Oceanië", "pijpen (rookgerei)", 30]
9: (4) ["origin", "Azië", "pijpen (rookgerei)", 193]
10: (4) ["origin", "Eurazië", "pijpen (rookgerei)", 9]
11: (4) ["origin", "Afrika", "hasjpijpen", 1]
12: (4) ["origin", "Azië", "opiumpijpen", 146]
13: (4) ["origin", "Amerika", "tabakspijpen", 11]
14: (4) ["origin", "Azië", "tabakspijpen", 193]
15: (4) ["origin", "Amerika", "pijpen (rookgerei)", 81]What had to be done next was that each array with a the same origin had to be pushed in a the same array. So i made a empty arrays for each origin, and one empty array to contain them all:
//create an empty array for each continent to store corresponding smoking tools in
let amerikaArray = [];
let azieArray = [];
let eurazieArray = [];
let afrikaArray = [];
let oceanieArray = [];
const completeArray = [];each origin array would now look something like this:
["origin", "Amerika", "opiumpijpen", 2, "origin", "Amerika", "hasjpijpen", 1, "origin", "Amerika", "tabakspijpen", 11, "origin", "Amerika", "pijpen (rookgerei)", 81]So what had to happen next was that type of smoking tool and the number that came after it needed to be in key value pair, and each origin should only appear once in the array of objects to be made. I did some research and found out that Object.entriesFrom could be used to turn an array into objects with key values. The example showed that each value that you wanted to turn into a key value pair should be an array with 2 values. The first value in this array would be transformed in the key and the second number in the array would be the corresponding value. So in order for me to us Object.fromEntries i had to group each smoking tool and the number that came behind that together. I did some googling and found the following function. I decided to try and use this function on one of my Arrays:
The chunk function i took from the medium example:
function chunk(array, size) {
const chunked_arr = [];
for (let i = 0; i < array.length; i++) {
const last = chunked_arr[chunked_arr.length - 1];
if (!last || last.length === size) {
chunked_arr.push([array[i]]);
} else {
last.push(array[i]);
}
}
return chunked_arr;
}The test:
console.log('chunk: ', chunk(amerikaArray, 2))The test output:
0: (2) ["origin", "Amerika"]
1: (2) ["opiumpijpen", 2]
2: (2) ["origin", "Amerika"]
3: (2) ["hasjpijpen", 1]
4: (2) ["origin", "Amerika"]
5: (2) ["tabakspijpen", 11]
6: (2) ["origin", "Amerika"]
7: (2) ["pijpen (rookgerei)", 81]Seeing this made me very happy as each smoking tool was now a array with it's corresponding number, but there was still the origin showing up multiple times. This Medium article gave me some further knowledge on Object.entriesFrom. It explains that unlike objects arrays do not require unique keys. They show the following example in the article:
const arr = [['a', 1], ['a', 2], ['c', 3]]
const entries = Object.fromEntries(arr)
// {a: 2, c: 3}The first value was lost because the first value a was dropped. This is not something that you alway want. But in this case it made my life a whole lot easier!
I decided to put it to the test and try the following:
console.log('Final Object: ', Object.fromEntries(chunk(amerikaArray, 2)))This gave me the following output:
{origin: "Amerika", opiumpijpen: 2, hasjpijpen: 1, tabakspijpen: 11, pijpen (rookgerei): 81}This was exactly what i needed! Now the only thing that had to be done was that every object had to be pushed in the new completeArray:
completeArray.push(Object.fromEntries(chunk(amerikaArray, 2)))
completeArray.push(Object.fromEntries(chunk(azieArray, 2)))
completeArray.push(Object.fromEntries(chunk(eurazieArray, 2)))
completeArray.push(Object.fromEntries(chunk(afrikaArray, 2)))
completeArray.push(Object.fromEntries(chunk(oceanieArray, 2)))Which gave me the following output:
completeArray:
(5) [{…}, {…}, {…}, {…}, {…}]
0: {origin: "Amerika", opiumpijpen: 2, hasjpijpen: 1, tabakspijpen: 11, pijpen (rookgerei): 81}
1: {origin: "Azië", waterpijpen: 103, pijpen (rookgerei): 193, opiumpijpen: 146, tabakspijpen: 193}
2: {origin: "Eurazië", tabakspijpen: 2, pijpen (rookgerei): 9}
3: {origin: "Afrika", tabakspijpen: 51, pijpen (rookgerei): 321, waterpijpen: 2, hasjpijpen: 1}
4: {origin: "Oceanië", tabakspijpen: 39, pijpen (rookgerei): 30}
length: 5
__proto__: Array(0)Hell Yeah! The data was now transformed in the way i needed it to be!
After finishing up I realised I had written a lot of code to do some simple data transformations. So i decided to do some research and found out you could also use variables as object keys and values. Since the first step in the data transformation proces was to use the value of a key as a key itself is decided to try this out:
let clean = {origin: element.origin, [element.type]: element.amount} gave me the following output:
{origin: "Eurazië", tabakspijpen: 2}So i just refactored what i first did in these steps. I learned that this was way to much code, and by refactoring this i had learned some new js functionalities that i didn't knew before.
After this i had to make sure every object was getting merged when the origin value was the same so i started out by writing a function to do this:
function mergeObjects(data){
//intialize empty objects for each origin
const amerikaObject = {};
const afrikaObject = {};
const asiaObject = {};
const eurAsiaObject = {};
const oceaniaObject = {};
const complete = [];
data.map( array => {
// console.log('ert: ', array)
// make objects like this: {origin: originName, opiumpijpen: number, tabakspijpen: number} etc.
// let cleanObject = {[array[0]]: array[1], [array[2]]: array[3]}
if(array.origin == 'Amerika') Object.assign(amerikaObject, array)
else if(array.origin == 'Afrika') Object.assign(afrikaObject, array)
else if(array.origin == 'Azië') Object.assign(asiaObject, array)
else if(array.origin == 'Eurazië') Object.assign(eurAsiaObject, array)
else if(array.origin == 'Oceanië') Object.assign(oceaniaObject, array)
});
complete.push(amerikaObject)
complete.push(afrikaObject)
complete.push(asiaObject)
complete.push(eurAsiaObject)
complete.push(oceaniaObject)
return complete
}After rendering the stacked bar chart i got some errors in the console:

I realised this was because not every object had every type of smoking tool. This is why d3 gave me an NaN error. I searched for a way to check if a key existed within an object and found this on stackOverflow. I started to with checking whether some keys exist like this:
function checkKeys(data){
//give non existent values a key with a value of 0
data.filter( object => {
if(!('waterpijpen' in object)) console.log(object)
})
}This worked, the function was now logging objects without waterpijpen. the next step was to add waterpijpen to these objects and give it a value of 0:
function checkKeys(data){
//give non existent values a key with a value of 0
data.filter( object => {
if(!('waterpijpen' in object)) Object.assign(object.waterpijpen = 0, object)
}This worked fine, so i added statements for every key that wasn't in every object by default:
function checkKeys(data){
//give non existent values a key with a value of 0
//resource to check if key exists: https://stackoverflow.com/questions/1098040/checking-if-a-key-exists-in-a-javascript-object
//resource to add a value of 0 : https://stackoverflow.com/questions/1168807/how-can-i-add-a-key-value-pair-to-a-javascript-object
data.filter( object => {
if(!('waterpijpen' in object)) Object.assign(object.waterpijpen = 0, object)
if(!('opiumpijpen' in object)) Object.assign(object.opiumpijpen = 0, object)
if(!('hasjpijpen' in object)) Object.assign(object.hasjpijpen = 0, object)
})
}