Looking for the exact opposite of path-to-regexp - javascript

I am building some API connector, and I'd like to be able to generate fetch URL easily.
My idea is to base my solution on the path-to-regexp lib syntax, so that for exemple injectParams('/foo/:hello', { hello: 'world'}) return '/foo/world
Is there an existing library to do such an injection ?

Here I'm replacing each key (having prefix :) with it's value in path variable.
function injectParams( path , obj )
{
for( var key in obj ){
var rgx = new RegExp(':' + key + '\\b', 'g');
path = path.replace( rgx, obj[key] );
}
return path;
}
var result;
result = injectParams('/foo/:hello', { hello: 'world'})
console.log( result );
// prints "/foo/world" in console
result = injectParams('/foo/:hello/:another', { hello: 'world',another:'wroking'});
console.log( result );
// prints "/foo/world/workng" in console
result = injectParams('/foo/:hello/:another/:hello/:hello', { hello: 'world',another:'wroking'});
console.log( result );
// prints "/foo/world/wroking/world/world"
result = injectParams('/foo/:a-b', { "a-b": 'world'})
console.log( result );

This will replace all occurrences of :key
const injectParams = (path, obj) =>
path.replace(/:\w+\??/g, x => {
const val = obj[x.replace(/:|\?/g, '')]
if(val != undefined) return val
else if (/\?/.test(x)) return ''
else throw new Error(`Value for '${x}' not specified.`)
})
console.log(injectParams('http://stackoverflow.com/:post?', {}))
console.log(injectParams('http://stackoverflow.com/:post?', {post: 1}))
console.log(injectParams('http://stackoverflow.com/:post', {})) // throw error
How it works
/:\w+/g find all :keys.
.replace(/:\w+\??/g, x => ... result of searching for /:\w+\??/g is passed to function and it has to decide with with value it should be replaced.
...obj[x.replace(':','')]) extract key value from object

path-to-regexp currently has compile api

Related

How to update item in array state in react native?

Can someone tell me how I can fix the "updated" issue. I can't update the items dynamically, I can only update manually.
Here is mode code
// Function (async) for rename the playlist and update
const renamePlaylist = async (el) => {
try {
const result = await AsyncStorage.getItem(STORAGE_KEY);
if (result !== null || result !== '') {
/* let filterArrayRenamePlaylist = playList.filter((val) => {
if (val.id !== el ) {
return val;
}
}); */
console.log(el, ' --------- el');
let renamePlayList = playList.filter((val) => {
if ( val.title === el ) {
let objIndex = playList.findIndex(obj => obj.title == val.title);
playList[objIndex].title = el; // it is not updated !
console.log(el , ' ----------- el 2');
};
});
// const updateList = [...renamePlayList ];
setUpdate(context, {addToPlayList: null, playList: [...renamePlayList] });
return await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(renamePlayList));
}
setRenamePlaylistModalVisible(false);
} catch (error) {
console.log(error)
}
}
When I try to manually update the playlist title like this:
let renamePlayList = playList.filter((val) => {
if ( val.title === el ) {
let objIndex = playList.findIndex(obj => obj.title == val.title);
playList[objIndex].title = 'This works'; // This works
// playList[objIndex].title = el; // This does not works
console.log(el , ' ----------- el 2');
};
});
I searched but couldn't find a solution to my problem.
lets take a closer look to your code and what your code is doing line by line, you made a few logical and code-style mistakes there:
let renamePlayList = playList.filter((val) => {
if ( val.title === el ) {
let objIndex = playList.findIndex(obj => obj.title == val.title);
playList[objIndex].title = el; // it is not updated !
console.log(el , ' ----------- el 2');
};
});
Issue 1:
Do not write misleading functions, filter function should not modify anything, nobody will expect it to do that, side-effects are bad.Please, consider to separate Filtering and Modifying logic
Issue 2:
Filter function should return a value, true or false. In your case it returns nothing, void, so in the snippet above your renamePlayList will be an empty array, [].
Issue 3: The logical issue itself.
You do not need to seek objIndex, because you have the val element from same array AND array filter function itself do provide index of currently processing element, take a look at the docs please: https://www.w3schools.com/jsref/jsref_filter.asp (Syntax section)
playList[objIndex] is absolutely equal to your val object on first matching val, if you have more elements with same title - your findIndex code will return the first found element only, all the rest elements will be unchanged
Your val.title === el statement and playList[objIndex].title = el; makes no sense due to title is el in any case, like title was el and you are reasigning it to el, if it was "abc" reasigning it to "abc" will not change anything. Your playList[objIndex].title = 'This works'; // This works works exactly because you are setting a different value.
Based on that i would recommend changing your filter function to
playList
.filter((val) => val.title === el)
.forEach((val) => val.title = "new title or whatever you need here, not el btw")
This code will modify elements in playList, not sure if it is expected but if not - just make a deep clone of it before processing.
your setUpdate will need a little change also after that:
setUpdate(context, {addToPlayList: null, playList: [...playlist] });
Please, double check what your code is doing and what are you trying to achieve. My guess is that your top level function needs 2 parameters, like oldTitle and newTitle so it would be
playList
.filter((val) => val.title === oldTitle)
.forEach((val) => val.title = newTitle)

How to set Key/Value pair to chrome.storage.sync

I am trying something simple with a Chrome Extension, but the documentation I have found is not clear enough regarding the structure of the methods to use the chrome storage. I have the following code (generic) to store and retrieve some values:
var value = 561;
var key = "abc";
chrome.storage.sync.set({ [key] : value });
chrome.storage.sync.get(key, ({ result }) => {
console.log("value received is: " + result); // This does not work, result = undefined
});
If I want to retrieve the key added to the store, what is wrong with the previous code?
When using { result } you are extracting result from the first parameter being passed.
Should be instead:
var value = 561;
var key = "abc";
chrome.storage.sync.set({[key]: value });
chrome.storage.sync.get(key, ({ abc }) => {
console.log("value received is: " + abc);
});
// or
chrome.storage.sync.get(key, (result) => {
console.log("value received is: " + result.abc);
});
TLDR;
In JavaScript, when adding { result } you are extracting that variable from the underlying object in this case, the parameter.
Assuming the object is:
const results = {
a: 1,
b: 2,
c: 3
}
You can simplify the parameters to be like:
const { a, b ,c } = results
which is similar to:
const a = results.a
const b = results.b
const c = results.c
Since the first argument in your function is now { result }, it is expecting that key to exist in the first argument of that function. But it doesn't, it should be instead{abc}

Map empty after set values in forEach. Debugger shows the array iterated over isn't empty

If i have this code
ArbFileProvider.js
const fs = require("fs");
class ArbFile {
constructor(arbFile) {
this.arbFile = arbFile;
}
async read() {
const arbFileContents = await fs.promises.readFile(this.arbFile);
this._arbFileObject = JSON.parse(arbFileContents.toString());
console.log(this._arbFileObject);
this._getRessources();
console.log(JSON.stringify(this.ressources));
console.log(JSON.stringify(this.properites));
}
_getRessources() {
const ressourcesKeys = Object.keys(this._arbFileObject).filter(
key => key.match(/^#{1,2}/g) == undefined
);
console.log("ressourcesKeys", ressourcesKeys);
this.ressources = new Map();
ressourcesKeys.forEach(key => {
this.ressources.set(key, {
value: this._arbFileObject[key],
...(this._arbFileObject[
Object.keys(this._arbFileObject).find(val => val == `#${key}`)
] || {})
});
});
console.log("ressources", JSON.stringify(this.ressources));
const propertiesKeys = Object.keys(this._arbFileObject).filter(
key => key.match(/^##/g) != undefined
);
this.properites = new Map();
propertiesKeys.forEach(key => {
this.properites.set(key.replace(/^##/g, ""), this._arbFileObject[key]);
});
}
}
module.exports = ArbFile;
at that point
console.log(JSON.stringify(this.ressources));
this.ressources is an empty map. I don't know why. In the debugger I can clearly see, that is has 55 entries. I have already tried to log for example the Object.keys() of it, but it is an empty Array too.
The arbFile parameter at the constructor is an path to an json-like ARB file.
To call that class, I create an new instance of ArbFile and then call read() on it.
Well here's why. This is Chrome console output. I'm really surprised, but apparently you cannot JSON.stringify a map!
var m = new Map();
m.set("a", undefined)
Map(1) {"a" => undefined}
m.set("b", "B")
Map(2) {"a" => undefined, "b" => "B"}
m
Map(2) {"a" => undefined, "b" => "B"}
JSON.stringify(m)
"{}"
Instead, try just logging the map directly without JSON.stringify.

Dificulties with JSON.parse?

I've been talking to my dev duck for the past few hours and cannot for the life of me rubber ducky debug this code. Basically, it returns [object Object] for a sub object in JSON. The juicy part is that if I copy and paste the logged raw JSON text before its parsed, and then parse it, it parses fine.
Heres the aforementioned code, with the values being fed in:
/*
We are feeding in:
{
'src' : './template.html',
'format' : 'plain',
'input' : {
'noun' : 'World'
},
'replace' : 'templateExampleSkeleton'
}
*/
// Locals.
let tmpScripts:Array<string> = [];
let tmpScriptIds:Array<string> = [];
let tmpStrings:Array<string> = [];
let tmpStringIds:Array<string> = [];
// Replace scripts with placeholder IDs, and store their contents in a temporary location.
// They will be restored later, because they would cause issues with the JSON parser.
// This isn't used in this case but is used in general.
args = args.replace(/js{{(.|\s)*}}/g, (substring:string) => {
let tmpScriptId:string = this.#utils.genRandomId(false, tmpScriptIds);
tmpScripts.push(substring.replace('js{{','').replace('}}',''));
return `%%{{${tmpScriptId}}}%%`;
})
// Replace 's with "s.
.replace(/'/gm, '"')
// Replace whitespace.
.replace(/(\s|\n|\t|\r)*/gm, '')
// Restore the strings using their IDs.
.replace(/##{{.{32}}}##/gm, (substring:string) => {
let tmpStringValue:string = '';
tmpStringIds.forEach((id:string, i:number) => {
if (substring.includes(id)) tmpStringValue = tmpStrings[i];
});
return tmpStringValue;
});
// Add curly brackets so that the JSON parser doesn't yell.
args = '{' + args + '}';
console.log(args); // ==> {"src":"./template.html","format":"plain","input":{"noun":"World"},"replace":"templateExampleSkeleton"}
// Parse the arguments as JSON.
let argsJson = JSON.parse(args);
// Using the new object, iterate through its keys in order to
// restore the scripts that were removed for parsing as JSON.
// This isn't(?) used in this case but is used in general.
Object.keys(argsJson).forEach((argKey, i) => {
argsJson[argKey] = argsJson[argKey].toString().replace(/%%{{.*}}%%/gm, (substring:string) => {
substring = substring.replace(/%%{{/, '').replace(/}}%%/, '');
let tmpScriptValue:string = '';
tmpScriptIds.forEach((id:string, i:number) => {
if (id === substring) tmpScriptValue = tmpScripts[i];
});
return tmpScriptValue;
});
});
// Log the object for debug.
console.log(argsJson); // ==> Object { src: "./template.html", format: "plain", input: "[object Object]", replace: "templateExampleSkeleton" }
Any help is very appreciated :^)
To close this question with an answer, as pointed out by #LexWebb:
Object.keys(argsJson).forEach((argKey, i) => {
argsJson[argKey] = argsJson[argKey].toString().replace(/%%{{.*}}%%/gm, (substring:string) => {
substring = substring.replace(/%%{{/, '').replace(/}}%%/, '');
let tmpScriptValue:string = '';
tmpScriptIds.forEach((id:string, i:number) => {
if (id === substring) tmpScriptValue = tmpScripts[i];
});
return tmpScriptValue;
});
});
Should be:
Object.keys(argsJson).forEach((argKey, i) => {
if (typeof argsJson[argKey] === 'string') {
argsJson[argKey] = argsJson[argKey].replace(/%%{{.*}}%%/gm, (substring:string) => {
substring = substring.replace(/%%{{/, '').replace(/}}%%/, '');
let tmpScriptValue:string = '';
tmpScriptIds.forEach((id:string, i:number) => {
if (id === substring) tmpScriptValue = tmpScripts[i];
});
return tmpScriptValue;
});
}
});

using json objects in URLSearchParams

Is it possible to somehow append json objects onto a URLSearchParams object?
So instead of:
urlSearchParams.append('search', 'person');
it's:
urlSearchParams.append({search: "person"});
My answer courtesy of Darshak Gajjar's answer
Can use json objects via this way:
let test_this = [{"search": "person"}, { search: "another person"}];
var json = JSON.stringify(test_this);
urlSearchParams.append("myobj", json);
return this.http.post(this.post_url, urlSearchParams, options) //options being your own RequestOptions
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Something like this might work urlSearchParams = Object.assign(urlSearchParams, {search: "person"});
EDIT: Alternate solution using vanilla javascript. Also, I thought URLSearchParams was just a normal js object, but in fact you have to use get, set and append to access properties.
var params = new URLSearchParams("a=apple&b=balloon");
var parametersToAdd = {c: "car", d: "duck"};
for(key in parametersToAdd)
params.append(key, parametersToAdd[key]);
console.log(params.get('c'));
console.log(params.get('d'));
EDIT bis:
.append() supports to re-use the same key/parameter name, while .set() would have overwritten a previous value.
May be using below code you can pass entire json object in URL Search param
var json = JSON.stringify(myObj);
this.http.get('url'+'?myobj='+encodeURIComponent(json))
There's no API for that. You just need to enumerate over the properties and append them manually, for example using the following function:
function appendParams(params: URLSearchParams, obj: any) {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
params.append(key, obj[key])
}
}
}
appendParams(urlSearchParams, { search: 'person' });
Want to share my answer for Angular2 with the option of sending an Array
This is how I use this get function:
this.get('/api/database', {
'age': [1,2,3,4]
})
And the service is something like:
get(url, _params = {}) {
let params = this._processParams(_params);
return this.http.get(url, params).toPromise();
}
_processParams(obj: any) {
/* Convert this
{ age: [1,2,3] }
To:
param.append('age', 1);
param.append('age', 2);
param.append('age', 3);
*/
let params = new URLSearchParams();
for (let key in obj) {
for (let index in obj[key] ) {
params.append(key, obj[key][index]);
}
}
return {
search: params
};
}
Super simple answer/example:
// Create:
const params = new URLSearchParams({
a: 1,
b: 2
})
// OR
// const params = new URLSearchParams("a=1&b=2")
// Append
params.append('c', 'woohoo') // Note: if c param already exists in params, this will replace it (won't be adding new param if already exists, hence no duplications)
console.log(params.toString())
// Prints: 'a=1&b=2&c=woohoo'
Here is my approach. We have a simple requirement where the object is only a key value pair where the value might be a string or an array. We haven't found a use case for nested objects.
So let's say we want to convert this object into a query string or vice versa:
const input = {
ini: 'ini',
itu: 'itu',
ayo: ['desc', 'asc'],
}
Then we have two functions to parse & stringify:
function stringify(input) {
const params = new URLSearchParams();
for (const key in input) {
if (Array.isArray(input[key])) {
input[key].forEach(val => {
params.append(key + '[]', val)
})
} else {
params.append(key, input[key]);
}
}
return '?' + params.toString();
}
function parse(input) {
const payload = {};
const params = new URLSearchParams(input);
for(let [key, val] of params.entries()) {
if (key.endsWith('[]')) {
key = key.replace(/\[\]$/, '');
if (payload[key]) {
payload[key].push(val);
} else {
payload[key] = [val]
}
} else {
payload[key] = val;
}
}
return payload;
}
So the result should be "?ini=ini&itu=itu&ayo%5B%5D=desc&ayo%5B%5D=asc". This is similar to the array format that is found in this example.
Please note that this might not be battle tested, but for us we don't really have complicated object structure.
const url = new URL('/', location.origin);
console.log(url.href); // https://stackoverflow.com/
Object.entries({this:4,that:1}).forEach((item)=>{
// note .set replaces while .append will duplicate params
url.searchParams.append(...item);
});
console.log(url.href); // https://stackoverflow.com/?this=4&that=1

Categories