'Best way to replace strings inside an JavaScript object
I have an unknown
javascript object (let's call it IncompleteObject
just for readability) and an array of IVariables
which can be anything, but in the following format:
key: string
value: unknown
Example:
IVariables:
[
{ key: 'someObject', value: { some:'value' },
{ key: 'name', value: 'another value' },
{ key: 'lastName', value: 'this variable exists but wont be used' }
]
IncompleteObject:
{
ID: "SGML",
SortAs: "{{someObject}}",
GlossTerm: "Standard Generalized Markup Language",
Acronym: "The acronym is {{name}}",
GlossSee: "markup"
}
Expected Result:
{
ID: "SGML",
SortAs: {
some:'value'
},
GlossTerm: "Standard Generalized Markup Language",
Acronym: "The acronym is another value",
GlossSee: "markup"
}
The solution i thought was stringfying the object, replacing everything as strings and then trying to parse as JSON again (if it fails, it fails), but i'm wondering if there is a better solution to this... Also idk how to make it so that the SortAs for example becomes an object and not a string
Thanks!
*Notes:
- The object won't necessarily have all variables, like the example.
- The {{}} format is just an idea, i have no problem changing it since {} is used in JSON
Solution 1:[1]
I ended up doing with JSON.stringfy and some regex.
Here is how i did it:
const incompleteObject = {
NoVariable: "Something",
JustTheObject: "{{someObject}}",
JustTheString: "{{name}}",
IntegerValue: "{{integer}}",
StringInsideOtherString: "Bla bla bla {{name}}",
ObjectInsideString: "The acronym is {{name}}",
VariableThatDoesntExist: "{{thisdoesntexist}}",
ArrayToTestNestedStuff: [
{
JustTheObject: "{{someObject}}",
JustTheString: "{{name}}"
},
{
JustTheObject: "{{someObject}}",
JustTheString: "{{name}}"
}
]
}
const variablesToSubstitute: Record<string, unknown> = {
someObject: { some:'value' },
name: 'another value',
integer: 2,
lastName: 'this variable exists but wont be used'
}
const result = replaceVariables(incompleteObject, variablesToSubstitute)
console.log(JSON.stringify(result, null, 2))
const replaceVariables = (objectToReplace: unknown, variables: Record<string, unknown>) => {
let stringfiedObject = JSON.stringify(objectToReplace)
stringfiedObject = replaceEntireProperty(stringfiedObject, variables)
stringfiedObject = replaceSubstring(stringfiedObject, variables)
const result = JSON.parse(stringfiedObject)
return result
}
const replaceEntireProperty = (stringfiedObject: string, variables: Record<string, unknown>) => {
stringfiedObject = stringfiedObject.replace(/"{{[\w]+}}"/g, (substring: string, ...args: any[]) => {
const substringWithoutBracesAndComma = substring.substring(3, substring.length-3)
return JSON.stringify(variables[substringWithoutBracesAndComma] ?? removeAllUnescapedCommas(substring))
})
return stringfiedObject
}
const replaceSubstring = (stringfiedObject: string, variables: Record<string, unknown>) => {
stringfiedObject = stringfiedObject.replace(/{{[\w]+}}/g, (substring: string, ...args: any[]) => {
const substringWithoutBraces = substring.substring(2, substring.length-2)
return removeAllUnescapedCommas(JSON.stringify(variables[substringWithoutBraces] ?? substring))
})
return stringfiedObject
}
const removeAllUnescapedCommas = (stringToUnescape: string) => {
return stringToUnescape.replace(/(?<!\\)\"/g, "")
}
Here is also a GitHub repository with the solution working (Since its typescript, i didnt managed to add the code snippet): https://github.com/vceolin/replace-variables-inside-object
Solution 2:[2]
You won't be able add objects during replacing a string, so you'd need do the check if the tag is entire string or not beforehand:
const IVariables = [
{ key: 'someObject', value: { some:'value' }},
{ key: 'name', value: 'another value' },
{ key: 'lastName', value: 'this variable exists but wont be used' }
];
const incompleteObject = {
ID: "SGML",
SortAs: "{{someObject}}",
GlossTerm: "Standard Generalized Markup Language",
Acronym: "The acronym is {{name}}",
GlossSee: "markup {{non-existing-tag}}",
blah: "{{name}} and again {{name}}"
}
//convert array into object
const dataVariables = IVariables.reduce((a, b) => (a[b.key] = b.value, a), {});
const reg = /({{([^}]+)}})/;
for(let key in incompleteObject)
{
const data = incompleteObject[key],
variable = data.match(reg);
if (!variable)
continue;
if (variable[1] == data) //if entire string a tag, don't use string replace
incompleteObject[key] = dataVariables[variable[2]] || variable[1];
else
incompleteObject[key] = data.replace(new RegExp(reg, "g"), (a, b, c) => dataVariables[c] || b)
}
console.log(incompleteObject);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Vitor Ceolin |
Solution 2 |