Skip to content

#1 Cleaning JSON

Rick Groot edited this page Nov 12, 2020 · 1 revision

File location

De files die ik bij deze opdracht heb aangemaakt zijn te vinden in de map "CleanJson". Hier heb ik alles ingezet omdat het dan niet in de weg komt te staan van de hoofdopdracht waar ik later mee ga werken.

Filtering JSON file

Uit de dataset van datavisualisatie ben ik een aantal dingen gaan filteren. Zo het ik verschillende objecten kunnen filteren die bijvoorbeeld geen rijbewijs hebben. Om dit te doen moest ik eerst het JSON bestand importen naar mijn js bestand. Daarna kon ik door middel van parsen de dataset filteren. Onderstaande code is een voorbeeld hiervan:

const schoenen = JSON.parse(json).filter(function (entry) {     //filters people with 4 pairs of shoes and no drivers licence
    if (entry.hoeveelPaarSchoenen.startsWith('4') && entry.rijbewijs.endsWith('ee')) {
        return entry;
    }})    
  
console.log('er hebben ' + rijbewijs.length + ' mensen geen rijbewijs');    //puts results in the console
console.log('en ' + schoenen.length + ' van die mensen hebben 4 schoenen'); //puts results in the console
//er hebben 36 mensen geen rijbewijs
//en 2 van die mensen hebben 4 schoenen

Cleaning JSON file

Nadat ik wat dingen heb geoefent met het filteren ben ik gaan kijken naar het opschonen van de data. Hiervoor heb ik de kolom hobbies gepakt, omdat er hier wat data in zit die niet goed is. Zo moeten er namelijk hoofdletters uit worden gehaald en arrays worden gesplitst.

toLowerCase()

Om te beginnen heb ik alle data omgezet naar lowercase, omdat er zo makkelijker mee te werken is. Dit heb ik gedaan met de volgende code:

element.toLowerCase()

replace()

Vervolgens wou ik alle punten vervangen met komma's. Dit is handig om te doen omdat er op de ene plek hobbies gescheiden stonden met punten, en op de andere plek met komma's. Als ik overal een komma van maak is het later makkelijker om de array te splitten. Ik heb gebruik gemaakt van wat regex om dit te kunnen doen. Code die ik heb toegevoegd:

element.toLowerCase().replace(/\./gi, ',')

split()

Vervolgens ben ik de array gaan splitten. Hierdoor houd ik alleen maar aparte arrays over met hobbies erin. Dit moet ik nu alleen nog maar bij elkaar toevoegen, zodat het 1 array word met alle hobbies daarin. Code die ik heb toegevoegd voor het splitten:

element.toLowerCase().replace(/\./gi, ',').split(',')

What do I have

Met alle code die ik heb gebruikt krijg ik nu meerdere arrays terug met hobbies van personen.
Dit is de code die ik tot nu toe heb:

const data = require('./dataviz.json');     //Imports json file
let column = "hobbies";        //put in column name from json file

let allAnswers = getAnswers(data, column); //calls function with datasheet and column variable

function getAnswers(answer, question) { //function that returns an array of answers of requested question
    return data.map(answer => answer[question])     //returns array via map
}

let allHobbies = getHobbies(allAnswers);    //calls function that puts all hobbies into one array

function getHobbies(answers) {
    return answers.forEach(element => {
        let arr = element.toLowerCase().replace(/\./gi, ',').split(',');
        
        console.log(arr)
    })
}

Wat deze code logt in de console:

[ 'gamen', ' sporten', ' feesten', 'fotografie', 'plantenzorg' ]
[ 'voetbal', ' sporten', ' gamen', ' ' ]
[ 'paardrijden', ' tekenen', ' gamen', ' plannen' ]
[ 'sporten', ' tekenen', ' zuipen', ' vormgeven' ]
[ 'video editen', ' gamen', ' airsoften' ]
[ 'talen leren ', ' ' ]
[ 'honkbal', " auto's", ' ' ]
[ 'gamen' ]
[ 'muziek samenstellen' ]
[ 'mountain bike', ' hardlopen', ' zwemmen' ]
[ 'programmeren', ' fifa', ' bier drinken' ]
[ 'gamen', ' voetballen' ]

and many more

And what now

Nu moet ik nog een aantal dingen voor elkaar krijgen. Ik heb deze onderwerpen voor mezelf (en het proces) hieronder op een rijtje gezet:

  • return de data naar variabele allHobbies
  • stop alle aparte hobbies in 1 array
  • haal spaties weg voor en na verschillende hobbies

Some issues

push()

Om mijn data nu in 1 array te krijgen heb ik de push method als eerst uitgeprobeerd. Dit werkte aardig goed, maar helaas niet perfect. Door deze method werden er arrays in een array gezet, wat niet de bedoeling is. Dit was mijn code:

function getHobbies(answers) {
    let arr = []

    answers.forEach(element => {
        arr.push(element.toLowerCase().replace(/\./gi, ',').split(','));
    })

    console.log(arr)
}

Deze code logt dit:

[
  [ 'gamen', ' sporten', ' feesten', 'fotografie', 'plantenzorg' ],
  [ 'voetbal', ' sporten', ' gamen', ' ' ],
  [ 'paardrijden', ' tekenen', ' gamen', ' plannen' ],
  [ 'sporten', ' tekenen', ' zuipen', ' vormgeven' ],
  [ 'video editen', ' gamen', ' airsoften' ],
  [ 'talen leren ', ' ' ],
  [ 'honkbal', " auto's", ' ' ],
  [ 'gamen' ],
  [ 'muziek samenstellen' ],
  [ 'mountain bike', ' hardlopen', ' zwemmen' ],
  [ 'programmeren', ' fifa', ' bier drinken' ],
  [ 'gamen', ' voetballen' ],
  [ 'gamen', 'muziek maken', 'lezen', 'schilderen' ],
  [ 'tekenen', ' series/ films kijken' ],
  [ 'programmeren', 'wielrennen', 'ontwerpen', 'crypto traden' ],

and many more

Zoals je hierboven kan zien worden er dus arrays in de array gepusht, maar ik wil dit graag 1 array hebben.

concat()

Hierna heb ik de concat method geprobeerd, maar dit werkte nog slechter. Nu werd er een lege array gelogd in de console, en ik weet niet precies waarom. Hier is de code:

function getHobbies(answers) {
    let arr = []

    answers.forEach(element => {
        arr.concat(element.toLowerCase().replace(/\./gi, ',').split(','));
    })

    console.log(arr)
}

//Logt: []

Rethinking

De push methode werkt tot nu toe het best, dus ik heb besloten om daar wat verder op te bouwen. Nu moet ik er alleen nog voor zorgen dat de values van de array worden gepusht, in plaats vaan de hele array. Dit kan ik doen door nog een forEach() te gebruiken, net als ik eerder heb gedaan in deze functie. Als ik dan vervolgens elk iten in de forEach() push naar een array die een scope hoger zit, zou het moeten lukken. Hier is de code:

function getHobbies(answers) {
    let arr = [];                   //creates an array witch wil hold all items

    answers.forEach(element => {    //loops over the big array of answers
        element                     //selects array that got passed through
        .toLowerCase()              //puts everything to lowercase
        .replace(/\./gi, ',')       //replaces all dots with commas
        .split(',')                 //splits values in arrays where there is a comma
        .forEach(childElement =>    //runs through every hobby in an answer
            arr.push(childElement));//pushes each individual hobby to array arr 
    });

    console.log(arr);       //logs arr to check data
};

Dit geeft de volgende data weer in de console:

[
  'gamen',               ' sporten',              ' feesten',
  'fotografie',          'plantenzorg',           'voetbal',
  ' sporten',            ' gamen',                ' ',
  'paardrijden',         ' tekenen',              ' gamen',
  ' plannen',            'sporten',               ' tekenen',
  ' zuipen',             ' vormgeven',            'video editen',
  ' gamen',              ' airsoften',            'talen leren ',
  ' ',                   'honkbal',               " auto's",
  ' ',                   'gamen',                 'muziek samenstellen',
  'mountain bike',       ' hardlopen',            ' zwemmen',
  'programmeren',        ' fifa',                 ' bier drinken',
  'gamen',               ' voetballen',           'gamen',
  'muziek maken',        'lezen',                 'schilderen',
  'tekenen',             ' series/ films kijken', 'programmeren',
  'wielrennen',          'ontwerpen',             'crypto traden',
  'voetballen',          ' tekenen',              '',
  'youtube',             ' twitch',               ' video editten',
  ' bullet journalen',   '',                      'data vis',
  'films',               ' gamen',                ' kickboksen',
  'schrijven',           ' zingen',               ' gamen',
  'tekenen',             ' koken',                ' gamen',
  ' longboarden',        'turnen',                ' dansen',
  '',                    'gamen',                 'lezen',
  'wandelen',            'lezen',                 ' netflix',
  ' hertog jan drinken', 'kitesurfen',            ' windsurfen',
  ' basketbal',          ' borrellen',            ' gamen',
  'netflix kijken',      ' tennissen',            ' varen',
  ' fotografie',         ' suppen',               ' lekker eten',
  ' op vakantie gaan',   ' koken',                '',
  'muziek luisteren',    'verven',                ' schrijven',
  ' lezen',              ' gamen',                ' gewichtheffen',
  ' vechtsport',         'gamen',                 'gloving',
  'ontwerpen',           ' tekenen',              ' lezen',
  'gitaar spelen',
  ... 211 more items
]

Alles wat er nu nog te doen staat is de lege velden weghalen, en de velden zonder waarde.

Clearing spaces

Om de spaties weg te halen heb ik een aantal dingen geprobeerd. Eerst heb ik geprobeerd om de spaties weer weg te halen met regex direct achter mijn push. Dit deed ik op dezelfde manier als eerder in mijn code:

function getHobbies(answers) {
    let arr = [];                       //creates an array witch wil hold all items

    answers.forEach(element => {        //loops over the big array of answers
        element                         //selects array that got passed through
        .toLowerCase()                  //puts everything to lowercase
        .replace(/\./gi, ',')           //replaces all dots with commas
        .split(',')                     //splits values in arrays where there is a comma
        .forEach(childElement => {      //runs through every hobby in an answer
            arr.push(childElement       //pushes each individual hobby to array arr 
                .replace(/\s/gi, ''))   //removes all spaces                       <--------removed spaces here
            });
    });

    console.log(arr);       //logs arr to check data
};

Waar ik toen achter kwam was dat dan natuurlijk alle spaties worden weggehaald. Dus ook de spaties die tussen woorden in zitten, zo had ik nu dus bijvoorbeeld een hobby genaamd videoediten, wat er niet heel netjes uitziet.

Vervolgens ben ik het weghalen van spaties op een andere plek gaan doen in mijn code. Niet meer in het forEach() blok, maar nog wel binnen de functie getHobbies(). Om dit allemaal weg te kunnen halen ben ik aan de slag gegaan met een aantal if statements binnen een for loop. Op deze manier wordt er gekeken naar elk individueel item in de array, en wordt er gecheckt of die ergens niet aan voldoet door de if statements. De code van de for loop met if statements ziet er als volgend uit:

for (i = 0; i < arr.length; i++) {      //for loop that goes through hobby array
        if (arr[i].startsWith(' ')) {       //if the string starts with a space
            arr[i] = arr[i].substring(1);   //replaces the string from 2nd character, so without a space
        } 
        if (arr[i].startsWith('  ')) {      //if the string starts with 2 spaces
            arr[i] = arr[i].substring(2);   //replaces the string from 3nd character, so without spaces
        } 
        if (arr[i].length < 1) {            //if the string is shorter then 1 character
            arr[i] = arr.pop(arr[i])        //pops the item out of the array
        }
    }

Clean data

Als laatste stap heb ik mijn data gereturnd naar de variabele waar het werd aangeroepen. Deze variabele kan ik vervolgens weer loggen naar de console, en daar krijg ik een array terug met allemaal clean data. Ook heb ik nog even snel een sort() toegevoegd bij de return, zodat de data netjes terugkomt.
Data die wordt teruggestuurd:

$ node .
37 mensen drinken alleen koffie
er hebben 36 mensen geen rijbewijs
en 2 van die mensen hebben 4 schoenen
[
  ' chillen',
  ' films kijken',
  ' fitness',
  ' fotografie',
  ' gamen',
  ' gamen',
  ' sporten',
  ' tekenen',
  'airsoften',
  'animeren',
  "auto's",
  'bakken',
  'basketbal',
  'basketbal',
  'basketball',
  'beven',
  'bezig zijn met ecommerce',
  'bier drinken',
  'bier drinken',
  'bier drinken',
  'boksen',
  'borrellen',
  'bouwpakketten maken',
  'boxen',
  'bullet journalen',
  'cello spelen',
  'chillen',
  'chillen met vrienden',
  'coderen',
  'creatief',
  'crypto traden',
  'dansen',
  'dansen',
  'dansen',
  'data vis',
  'datavisualisatie maker',
  'design',
  'designen',
  'dj',
  'drinken',
  'drinken',
  'drinken',
  'eten',
  'eten',
  'familie',
  'feesten',
  ... 244 more items
]

Setting up a small server

Om mijn code online te laten zien heb ik besloten om een lokale server te starten. Dit is voor mij de makkelijkste manier omdat ik zo nog steeds met node kan werken voor dit kleine cleaning project. Om de data te laten zien ben ik aan de slag gegaan met een framework, express. Ik heb hiervoor gekozen omdat ik hier al een beetje ervaring mee heb, en ik er dus sneller mee werk voor nu.

Setting it up

Om alles te laten werken moet ik een aantal modules installeren. Dit doe ik via npm in de console. Express en EJS zijn nodig voor deze feature.

$ npm install express
$ npm install ejs

Vervolgens moet ik alle modiles requiren, en daarbij ook een localhost port opzetten. Dit heb ik gedaan door de volgende code toe te voegen aan mijn clean.js bestand.

const express = require('express');         //use express to render stuff
const app = express();
const port = 8080;                          //set up a localhost port

//at end of code
app.listen(port, () => console.log(`Example app listening on port ${port}!`));  //puts rendered data at localhost:8080/clean

Rendering the data

Om iets te kunnen laten zien in de browser heb ik een bestand nodig wat ik kan renderen. Hiervoor gebruik ik een EJS bestand. In een ejs bestand kan ik javascript gebruiken om meerdere componenten te laden, of in mijn geval kan ik alle hobbies uit mijn opgeschoonde array renderen. Dit is de code uit mijn EJS bestand:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Clean json data</title>
    <script src="../node_modules/jquery/dist/jquery.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="clean.js"></script>
</head>
<body>
    <% for (var index = 0; index < data.length; index++) { %>
        <p><%= data[index] %></p>
      <% } %>
</body>
</html>

Vervolgens hoef ik alleen dit bestand alleen nog maar aan te roepen via mijn javascript bestand. Ook moet ik daar de juiste data aan meegeven, omdat het anders niet in het bestand kan worden gebruikt en error messages gaat geven. Hiermee render ik de pagina:

app
    .set('view engine', 'ejs')              //initialize ejs
    .use('/', clean);                       //call function clean() when on localhost

function clean(req, res, next) {    
    res.render('clean.ejs', {data: allHobbies});    //renders clean.ejs, with the data from allHobbies array
}

What could be better

De data heb ik nu opgeschoond, maar er kunnen nog dingen worden verbeterd in mijn code. Zo staat de code voor het filteren niet in een functie, wat wel de bedoeling is met functional programming. Dit zou ik nog in een functie kunnen zetten zodat de code netter is. Ook is de functie getHobbies() erg groot. Deze zou ik nog uit elkaar kunnen halen zodat er meerdere korte functies ontstaan, die allemaal hun eigen functie hebben. Ook kan ik op deze manier code makkelijk kopiëren en plakken bij andere projecten, omdat elke functie dan minder complex is.

Clone this wiki locally