functional patterns - NathanNeelis/frontend-data GitHub Wiki
Defining most important code on top
So at the beginning of my code, I am defining the most important variables and functions at the top. I do this because it's the best way to understand how my code works. In the cleaning process of the survey data, all my variables are at the top that returns a cleaned array of data. The variable is defined by a function that cleans the data. This way you can quickly scope out which variable contains which data and which function does which cleaning process. Below a code sample of this functional pattern.
let secondColumnName = "oogKleur";
let allEyeColorData = getData(dataSurvey, secondColumnName);
let eyeColorDataLowerCase = toLowerCase(allEyeColorData);
function getData(answers, question) {
let answerData = [];
for (answer of answers) {
answerData.push(answer[question]);
}
return answerData; // Array with all the data in the specific column
};
function toLowerCase(arr) {
let newCleanData = arr.map(x => x.toLowerCase());
return newCleanData;
}
Always return a value
My functions always return a value. Because of this functional pattern the reader can always look at what's being returned, this helps by understanding the code nice and easy.
Below a code example of two functions that return a value.
function removeSpaces(arr) {
let cleanData = arr.map(keys => keys.replace(/ /, ''));
return cleanData; // Array without spaces in strings.
}
function hexCheck(arr) { // Check if arrayItems start with #
let cleanData = arr;
for (result in cleanData) {
if (arr[result].charAt(0) !== '#') { // If the first char is nog a #
cleanData[result] = '#' + cleanData[result] // add the # infront of the string
}
}
return cleanData // return array with added #
}
Always use names that explain the process
Using this functional pattern makes the reading process of the code even easier. By using names that tell you what the function or variable does or contains you get a quicker understanding of the code. For example my function "replaceColorNamesToHexcolors" clearly indicates that this function replaces color names with hex color codes. I wouldnt need to know how the function works but I could instantly use it to pass in color names and receive hex colors back.
function replaceColorNamesToHexcolors(arr) {
var cleanData = arr.map(
x => {
return x
.replace(/blauw/, '#0000FF')
.replace(/blue/, '#0000FF')
.replace(/groen/, '#008000')
// removed some replaces to shorten the example
.replace(/paars/, '#800080')
.replace(/purple/, '#800080')
.replace(/grijs/, '#808080')
.replace(/gray/, '#808080')
.replace(/wit/, '#FFFFFF')
.replace(/white/, '#FFFFFF');
});
return cleanData; // Array with colornames converted to hex colors.
}
The same for my variables, the name indicates which values are attached to that variable. For example "eyeColorDataLowerCase" clearly indicates that it contains all eye color data in lowercase. Also in the function, you can see that I pass in all the eye color data.
let eyeColorDataLowerCase = toLowerCase(allEyeColorData);
Pure functions
In my code, I am using pure functions. All functions take a parameter as a value to work with and then return a (new) value.
The example below takes an argument "arr" which is an array of data and takes that data to filter out all values that are not empty and put these in a new array and returns that new array.
function removeEmptySlots(arr) {
let cleanData = arr.filter(keys => keys != "");
return cleanData;
}
Higher order functions
I wrote some documentation on the higher-order functions here in my wiki. Here I will give two examples of high-order functions that I used to create new arrays instead of changing the original one.
filter
Same code example as above, it takes an array of data, filters out what I want to have and returns those values in a new array.
function removeEmptySlots(arr) {
let cleanData = arr.filter(keys => keys != "");
return cleanData;
}
map
As I already used this example above, I shortend it to just one value to replace. In this example it takes all values from an array and changes the values to what I want to change, in this case the color names to hex color codes. After changing the values it returns all the values in a new array.
function replaceColorNamesToHexcolors(arr) {
var cleanData = arr.map(
x => {
return x
.replace(/blauw/, '#0000FF')
});
return cleanData; // Array with colornames converted to hex colors.
}
Immutability
Instead of modifying the original data array I always create a new variable with the changed data.
I change them by using higher order functions like map() and filter().
As you can see in the code below I create variables that are attached to functions to change the array with each variable.
let accessPointDataArray = filterData(prData, 'accessPoints'); // Array with all accesPoint data
let accessPointDataArrayClean = removeOuterArray(accessPointDataArray); // Array with all accesPoint data without outer array
let accessPointDataArrayFixed = fixEmptyValues(accessPointDataArrayClean); // Array with all missing values set to "0"
Composition
To make my code more readable I used modules to create my bar chart.
In the code example below you can see that I import two functions from the transfrom file.
I combined all data cleaning and transforming into the cleaningData function. But for the interaction happening in the visual I use the combineDoubleCities as well. Now instead of having 256 lines of code extra I only have 4 lines in my main file.
import {
combineDoubleCities,
cleaningData
} from './transform';
As I said above, I combined a lot of functions so I only have to export two functions to my main file.
These export functions are on the top of my transform document also for the composition so you can quickly find what you want to know coming from the main index file.
In the code example, you see how I pack all my functions into a new function that I export to the main index file which I use to clean and transform my whole dataset.
export function cleaningData(data) {
let prCapacity = getCapacity(data); // Array with all capacity data
let prCityArray = filterCity(data); // array with all city names
let prDescription = filterData(data, 'description'); // array with descriptions
let objectArray = wrap(prCityArray, prCapacity, prDescription); // combine capacity, cityname and description into one object
let randstadCities = selectRandstad(objectArray); // array with all randstad city data
let randstadClean = cleanRandStadData(randstadCities); // array transformed for data vis
let cleanData = listUnique(randstadClean); // Array with unique data points
return cleanData
}