How to remove empty query params using URLSearchParams? - javascript

I was working with query params, and got introduced to URLSearchParams. I am using it to form this kind of object to query,
const x = {
a: 'hello World'
b: 23
c: ''
}
let params = new URLSearchParams(x);
console.log(params.toString()) // a=hello+World&b=23&c=
Here, I dont want to have that c=, as it's ugly, and my API doesn't need that.
So, I want this result a=hello+World&b=23 (without empty query string)
But, I couldn't find anything on the MDN Web Docs.
How am I supposed to do that?
Doing the following doesn't work, as it seems to directly mutate the params which affects the forEach:
const x = {
a: 'hello World',
b: '',
c: ''
};
let params = new URLSearchParams(x);
params.forEach((value, key) => { // never reaches `c`
console.log(key, ' => ', value)
if (value == '')
params.delete(key);
});
console.log(params.toString());

You can iterate over the key-value pair and delete the keys with null values:
const x = {
a: 'hello World',
b: '',
c: ''
};
let params = new URLSearchParams(x);
let keysForDel = [];
params.forEach((value, key) => {
if (value == '') {
keysForDel.push(key);
}
});
keysForDel.forEach(key => {
params.delete(key);
});
console.log(params.toString());

A clean way I do it myself is as follows (using lodash):
import omitBy from 'lodash/omitBy';
import isEmpty from 'lodash/isEmpty';
const x = {
a: 'hello World'
b: 23
c: ''
}
const params = new URLSearchParams(omitBy(x, isEmpty));
// mixing other sets
const params = new URLSearchParams({
otherParam: 'foo',
...omitBy(x, isEmpty)
});

Simple way to delete useless params from query in JavaScript ES5+:
for (let param in query) { /* You can get copy by spreading {...query} */
if (query[param] === undefined /* In case of undefined assignment */
|| query[param] === null
|| query[param] === ""
) {
delete query[param];
}
}
return new URLSearchParams(query).toString();

In case your are working with the query as a string you can also filter it with a regex :
const query = "a=hello+World&b=23&c=&d=12&e="
query.replace(/\w+=&/g, '').replace(/&\w+=$/, '')
// "a=hello+World&b=23&d=12"

Related

Append string using regex at the end of URL values inside the nested object on Node.js

Sample object (the below object is a very large nested object):
const obj = { a: 1, url: "https://someurl.com", b: { c: 1, d: { otherUrl: "https://someotherurl.com"}};
I want to append some query parameter at the end of each URL, like ?d=1234.
I tried to do it, but the URLs are changed:
const convertedObj = JSON.stringify(obj);
let bufferObj;
const fullRegex = /url":"([^"]*)/g;
const fullMatch = convertedObj.match(fullRegex);
if(fullMatch) {
fullMatch.forEach((str, i) => {
bufferObj = convertedObj.replace(/(url|Url)":"([^"]*)/g, `${str}?d=1234`);
});
}
Is there a solution for this in Node.js using regex?
let convertedObj = JSON.stringify(obj);
const fullRegex = /(url|Url)":"([^"]*)/g;
const fullMatch = convertedObj.match(fullRegex);
if(fullMatch) {
fullMatch.forEach((str, i) => {
convertedObj = convertedObj.replace(str, `${str}?d=1234`);
});
}
console.log(convertedObj);

Single function for saving into localStorage?

How can we save and retain localStorage objects rather than creating multiple functions ? The first localStorage object get replaced with the new save. So to avoid that I have created a new function called saveLocalStorageDataTwo which is working. But how can we avoid creating multiple functions for saving data into the localStorage ? Is there any way ? Could some please advise ?
/* localStorage.js */
let localStorageData = {};
let localStorageDataTwo = {};
function saveLocalStorageData ({autoId, quoteId, taskId }) {
localStorageData = {
autoId: autoId,
quoteId: quoteId,
taskId: taskId,
}
return localStorageData
}
function saveLocalStorageDataTwo ({data}){
localStorageDataTwo = {
data : data,
}
return localStorageDataTwo
}
export { saveLocalStorageData, saveLocalStorageDataTwo };
// Saving to localStorage:
let localData = require("../../support/localStorage");
const data = "Some Data 260-255"
const localStorageData = localData.saveLocalStorageData({ autoId });
window.localStorage.setItem('localStorageData ', JSON.stringify(localStorageData ));
You simply don't use any strict params like {autoId, quoteId, taskId} just pass any arbitrary data.
Don't call something saveLocalStorageData if that's actually not what that function does.
Instead:
const LS = {
set(key, data) { localStorage[key] = JSON.stringify(data); },
get(key) { return JSON.parse(localStorage[key]); },
};
// export { LS };
// import { LS } from "./localstorage.js"
// Example saving multiple data in different LS keys
LS.set("one", {autoId: 1, quoteId: 2, taskId: 3});
LS.set("two", {autoId: 7});
// Example saving Object, and later update one property value
const data = {a: 1, b: 2, c: 3};
LS.set("single", data); // Save
LS.set("single", {...LS.get("single"), c: 99999}); // Update
console.log(LS.get("single")); // {a:1, b:2, c:99999}
// Example saving multiple data into a single Array:
LS.set("arr", []); // Save wrapper Array
LS.set("arr", LS.get("arr").concat({a: 1, b: 2}));
LS.set("arr", LS.get("arr").concat({e: 7, f: 9}));
console.log(LS.get("arr")); // [{"a":1,"b":2}, {"e":7,"f":9}]
jsFiddle playground
or in the last example instead of an Array you could have used an Object. It all depends on the needs.

Functional Programming: How to convert an impure function to a pure function when the input needs to be mutated

How can I create a pure function that updates an object that's been initialized in another function
something like:
parentFunction = (inputs: object[], condtionList: string[]) => {
const newObject = {f1: val1[], f2: val2[], f3: val3[]...}
inputs.forEach(input => {
if(condition1){
updateNewObject(condtion1, newObject, input, conditionList)
}
.
.
.
}
return newObject
}
The below function is impure as it's updating the newObject (mutating the input) how can I convert it to a pure function?
updateNewObject(condition, newObject, input, conditionList) {
const i = conditionList.indexOf(input.condition)
if(i === 0){
newObject.f1.push(input)
}
else if(i === 1) {
newObject.f2.push(input)
}
.
.
.
}
The above has no return value. It takes the newObject as input and based on some conditionals pushes values to the properties of the newObject. Is there anyway to make the above function pure? or do I have to rethink how I am updating newObject?
Functional programming is not only about purity, it's also about reusability and separation of concerns. It's difficult to write a big complex function, and even harder to test and maintain it. Following functional principles will help us avoid pain and discomfort.
Let's start by isolating the behaviours we care about. We identify functions push, update, and pushKey -
const identity = x =>
x
const push = (a = [], value) =>
a.concat([ value ])
const update = (o = {}, key = "", t = identity) =>
({ ...o, [key]: t(o[key]) })
const pushKey = (o = {}, key = "", value) =>
update(o, key, a => push(a, value))
This allows you to perform basic immutable transformations easily -
const d1 = { a: [1], b: [] }
const d2 = pushKey(d1, "a", 2)
const d3 = pushKey(d2, "b", 3)
const d4 = pushKey(d3, "c", 4)
console.log(d1) // { a: [1], b: [] }
console.log(d2) // { a: [1, 2], b: [] }
console.log(d3) // { a: [1, 2], b: [3] }
console.log(d4) // { a: [1, 2], b: [3], c: [4] }
Expand the snippet below to run the program in your own browser -
const identity = x =>
x
const push = (a = [], value) =>
a.concat([ value ])
const update = (o = {}, key = "", t = identity) =>
({ ...o, [key]: t(o[key]) })
const pushKey = (o = {}, key = "", value) =>
update(o, key, a => push(a, value))
const d1 = { a: [1], b: [] }
const d2 = pushKey(d1, "a", 2)
const d3 = pushKey(d2, "b", 3)
const d4 = pushKey(d3, "c", 4)
console.log(JSON.stringify(d1)) // { a: [1], b: [] }
console.log(JSON.stringify(d2)) // { a: [1, 2], b: [] }
console.log(JSON.stringify(d3)) // { a: [1, 2], b: [3] }
console.log(JSON.stringify(d4)) // { a: [1, 2], b: [3], c: [4] }
This allows you to separate your complex conditional logic into its own function -
const updateByCondition = (o = {}, conditions = [], ...) =>
{ if (...)
return pushKey(o, "foo", someValue)
else if (...)
return pushKey(o, "bar", someValue)
else
return pushKey(o, "default", someValue)
}
The advantages to this approach are numerous. push, update, and pushKey are all very easy to write, test, and maintain, and they're easy to reuse in other parts of our program. Writing updateByCondition was much easier because we had better basic building blocks. It's still difficult to test due to whatever complexity you are trying to encode, however it is much easier to maintain due to separation of concerns.
If you want updateNewObject to be pure, have it create a new object that clones the original, mutate that, and then return the new object.
updateNewObject(condition, oldObject, input, conditionList) {
const newObject = {...oldObject};
const i = conditionList.indexOf(input.condition)
if(i === 0){
newObject.f1 = [...newObject.f1, input];
}
else if(i === 1) {
newObject.f2 = [...newObject.f2, input];
}
.
.
.
return newObject;
}
Note how newObject.f1 = [...newObject.f1, input]; creates a new array - this ensures that we not only don't mutate the object directly, but we don't mutate any of its fields (arrays) and instead create new ones.
Then tweak parentFunction so that it uses the value of each returned updateNewObject call:
parentFunction = (inputs: object[], condtionList: string[]) => {
let newObject = {f1: val1[], f2: val2[], f3: val3[]...}
inputs.forEach(input => {
if(condition1){
newObject = updateNewObject(condtion1, newObject, input, conditionList)
}
.
.
.
}
return newObject
}
Classically, you'd create a new object with a new array with the new entry in it:
if(i === 0){
// v−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−v−− creates new object
newObject = {...newObject, f1: [...newObject.f1, input]}
// ^ ^−−−−−−−−−−−−−−−−−−−−−−^−−− creates new array
}
else if(i === 1) {
// v−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−v−− creates new object
newObject = {...newObject, f2: [...newObject.f2, input]}
// ^ ^−−−−−−−−−−−−−−−−−−−−−−^−−− creates new array
}
Then in parentFunction:
newObject = updateNewObject(condtion1, newObject, input, conditionList)
// ^^^^^^^^^^^^−−−−−−−−−−−−−−−−−−−−−− updates the value being returned
Or the update could be:
const name = i === 0 ? "f1" : (i === 1 ? "f2" : ""));
if (name) {
newObject = {...newObject, [name]: [...newObject[name], input]}
}
...though the nested conditional is a bit meh. :-)
Just copy/map the array to get a new one. Don't mutate the same one.

Get return Value from Object.keys

I have a function that searches an object, to see if a value exist in it but I can't get the value from it when I try to log it.
searchValue(object,value){
let valueExists = Object.keys(object).find(key => object[key].valueID === value );
console.log(valueExists);
}
You could use Object(o).values like :
let valueExists = Object.values(object).find(val => val === value )
console.log(valueExists)
JS Example :
function searchValue(object, value) {
let valueExists = Object.values(object).find(val => val == value)
console.log(valueExists)
}
let user = {
id: 12,
name: "aaa",
email: "aaaa#domain.com",
}
searchValue(user, "aaa")
If you already have value (as searchValue parameter) you don't need to "get" value because you already have it (below I simplify your implementation)
function searchValue(object, value) {
return Object.values(object).includes(value);
}
let obj = {a: "xx", b: "yy"};
let val = "yy"
if(searchValue(obj,val)) {
console.log("the object value exist and is:", val);
}
If you interest to find keys which are associated with value try this
function searchKeys(object, value) {
return Object.keys(object).filter(k=>object[k]==value);
}
let obj = {a: "xx", b: "yy", c:"xx"};
let val = "xx"
console.log("Keys contains 'xx' value:", searchKeys(obj,val));
To achieve expected result, use below option of using Object entries and looping with forEach for value
function searchValue(object,value){
Object.entries(object).forEach(v => {
if(v.indexOf(value) !== -1) console.log("valueExists")
});
}
let test = {
id : 11,
name: 'abc',
location: 'usa'
}
searchValue(test, 11)

Get wildcarnames from string template

Is there a quick way (a known way I mean) to get wildcard names from a string template?, something like...
const str = `Hello ${name}, today is ${weekday}!`;
getWildCards(str); // will return ['name', 'weekday']
I'm creating a translation tool and the translation function will not know the wildcards in advance.
EDIT:
Actually, it turns out there's a native way to extract the params from a template literal using tagged template literals, which allow you to parse template literals with a function of the form:
function tag(strings, param1, param2, ..., paramN) { ... }
So, if instead of using variables inside the expressions of your template literal (${ foo }) you use strings (${ 'foo' }), then if you do:
tag`${ 'a' }${ 'b' } - XXX ${ 'c' }`
strings will be ['', '', ' - XXX ', ''] and you will receive 3 params with values 'a', 'b' and 'c'.
You can return whatever you want from a tag function, so a good solution for your use case would be to return a pair [paramsList, closure], where the closure will be a function that receives an object (map) with the params being used in the original string literal and uses them to build the resulting string. Like this:
function extractParams(strings, ...args) {
return [args, dict => {
return strings[0] + args
.map((arg, i) => dict[arg] + strings[i + 1]).join('');
}];
}
const [params, translate] = extractParams`Hello ${ 'name' }, today is ${ 'weekday' }`;
console.log(params);
console.log(translate({ name: 'Foo', weekday: 'Barday' }));
ORIGINAL ANSWER:
Assuming that template string is wrapped into a function so that it doesn't throw a ReferenceError, and that you change a bit the format of your template strings so that the arguments used are always properties from an object, you could use a proxy for that:
Let's say you have something like this:
function getSentence(key, args = {}) {
// Note that for the proxy solution to work well, you need to wrap each of them
// individually. Otherwise you will get a list of all the params from all the
// sentences.
const sentences = {
salutation: (args) => `Hello ${ args.name }, today is ${ args.weekday }!`,
weather: (args) => `Today is ${ args.weather } outside.`,
};
return sentences[key](args) || '';
}
function extractParams(key) {
const params = [];
const proxy = new Proxy({}, {
get: (target, name) => {
params.push(name);
},
});
getSentence(key, proxy);
return params;
}
console.log(extractParams('salutation'));
Anyway, note this will only work if you just have one level depth in your args, otherwise you will need a proxy that returns another proxy that returns another proxy... and keep track of the path (prop.subprop...). They should also return a function that returns a string for the last property that will be interpolated in the resulting string.
function getSentence(key, args = {}) {
// Note that for the proxy solution to work well, you need to wrap each of them
// individually. Otherwise you will get a list of all the params from all the
// sentences.
const sentences = {
salutation: (args) => `Hello ${ args.name }, today is ${ args.a.b.c.d }!`,
weather: (args) => `Today is ${ args.weather } outside.`,
};
return sentences[key](args) || '';
}
function getProxy(params, path = []) {
return new Proxy({}, {
get: (target, name) => {
if (typeof name === 'symbol') {
params.push(path);
return () => ''; // toString();
} else {
return getProxy(params, path.concat(name));
}
},
});
}
function extractParams(key) {
const params = [];
getSentence(key, getProxy(params));
return params;
}
console.log(extractParams('salutation'));

Categories

Resources