-
Notifications
You must be signed in to change notification settings - Fork 0
Experiments
I've experimented with client-side modules. To do so I created a utility file which contains handy utility functions.
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 { splitString, cleanArray }
In my utils.js file I export 2 functions. However the file contains more than only 2 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;
})
}
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);
}
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;
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);
}
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;
}
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.
VALUES ?type `${types}`
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.