Skip to content

Experiments

SqueezyDough edited this page Nov 15, 2019 · 7 revisions

Modules

I've experimented with client-side modules. To do so I created a utility file which contains handy utility functions.

Import a file

import * as utils from "./utils.js"

On the top of the document you need to import your file you want to use. I've noticed that this only works properly if you import the file from the same directory. If it was in another directly I would get a 404 error.

Export functions

export { splitString, cleanArray }

In my utils.js file I export 2 functions. However the file contains more than only 2 functions.

Public functions

The split string and the clean array function are the function I want to export. They do use the other functions, but those are not important to be accessible from other files. Therefore I decided to keep them private.

export { splitString, cleanArray }

// splits a string using 1 or more separators
function splitString (string, ...separators) {
    let regEx = new RegExp(separators.join("|"))
    return string.split(regEx)
}

// replaces chars from string
// takes an array that requires cleaning as the first argument
// takes a second array of array(s) that contains an array with a target string and a new string
function cleanArray (array, filterOptions, library) {
    return array.map (item => {
        item = item.toLowerCase();

        // check if item is in library
        if (library) {
            item = filterString(item, library, false);
        }

        // filter other crap
        item = filterString(item, filterOptions, true);

        // capatalize the first character
        item = capitaliseFirstCharacter(item)

        return item;
    })
}

Private functions

These functions are only accessible by the utils file itself and can not be used among other files. That is because they are important for the public utility functions, but not important for my index.js file for example.

function filterString(string, filters, strict) {
    filters.forEach ( filter => {
        let oldString, newString = "";

        if (!strict && arrayIncludes(string, filter)) {
            oldString = string;

        } else {
            oldString = filter[0];
        }

        newString = filter[filter.length - 1]

        string = string.replace(oldString, newString);
    })

    return string;
}

function arrayIncludes (string, array){
    return array.some( testString => string.includes(testString))
};

function capitaliseFirstCharacter(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
}

Refactors

Check type of string

I tried to check the type of the string and add the correct property. However all types in the end were strings.

let obj = {}
    Object.entries(item)
         .forEach(([propValue]) => {
               if (typeof propValue.value === String) {
                    obj.Name = propValue.value
               }
               if (typeof propValue.value === Number) {
                    obj.Amount = propValue.value
               }
         })

         return obj;

So I refactored the code to check if the string contained only numbers. If so, I would know that this is the amount value and the other value is the name value. This is a edited version of Laurens's code that only cleaned the data. This example also transforms it to the right structure for d3.

let obj = {}
     Object.entries(item)
           .forEach(([key, propValue]) => {
                 if (utils.isStringANumber(propValue.value)) {
                       obj.Amount = propValue.value
                 } else {
                       obj.Name = propValue.value
                 }
            })

            return obj;

Transform and clean data

I first had a function that cleaned the data, but did it in a less functional matter. It added the country label and choCount in another object. This meant that the object had to had these values otherwise it wouldn't work.

dataController.fetchData = function(req, res) {
    const queryTypes = ["kleding"];

    dataController.fetchTypes(queryTypes).then( (data) => {
        let items = data.map (item => {
            const obj = {
                Name : item.countryLabel.value,
                Amount : parseInt(item.choCount.value),
            }
            return obj;
        })

        res.write(JSON.stringify(items) );
        res.end();
    })
}

I've used the example from Laurens to iterate over the key/value pairs. I expect a name and a number. That is how a bubble chart works in the first place. So I wrote a function that checks if the string contains only integers. I so, I know that that value is the amount.

 let obj = {}
     Object.entries(item)
          .forEach(([key, propValue]) => {
                if (utils.isStringANumber(propValue.value)) {
                     obj.Amount = propValue.value
                } else {
                     obj.Name = propValue.value
                }
            })

          return obj;
utilsController.isStringANumber = function(string) {
    return /^\d+$/.test(string);
}

Refactor cleanArray

As mentioned previously the cleannArray function did 2 different things. If you ook at this more closely, it actually did the same thing, but in a slightly different way. A strict mode and a non-strict mode. I refactored the function to make it more functional.

I now call the filterString() function and pass the filters/library and a boolean.

function cleanArray (array, filterOptions, library) {
    return array.map (item => {
        item = item.toLowerCase();

        // check if item is in library
        if (library) {
            item = filterString(item, library, false);
        }

        // filter other crap
        item = filterString(item, filterOptions, true);

        // capatalize the first character
        item = capitaliseFirstCharacter(item)

        return item;
    })
}

When its strict, it will exactly replace the first item from the array with the last. When not strict, it will look if the string contains the string and replace it with the last

function filterString(string, filters, strict) {
    filters.forEach ( filter => {
        let oldString, newString = "";

        if (!strict && arrayIncludes(string, filter)) {
            oldString = string;

        } else {
            oldString = filter[0];
        }

        newString = filter[filter.length - 1]

        string = string.replace(oldString, newString);
    })

    return string;
}

Removed code

Reusable query

I wanted to execute the same query for every new value in the array. For example I can add "Kleding" and "Wapen" to the array. The function will transform it to an acceptable query output.

'Kleding' 'kleding' 'Wapen' 'wapen'

I always add a first char uppercase value to the array because the Thesaurus is case-sensitive.

Query

VALUES ?type `${types}`

Function

function createTypeString(types) {
    let string = "";

    types.forEach( type => {
        type = type.toLowerCase();

        string = string
               + `'${type}'`
               + `'${utils.capitaliseFirstCharacter(type)}'`
    });

    return string;
}

After experimenting with the query further Ivo and I decided to use uri's instead because they are more accurate. This means I had no use for this function anymore.

Clone this wiki locally