1

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
10
  • in before: please read the problem before asking if "how to replace a string?" answers my question :) Commented May 13, 2022 at 11:42
  • So your question is already answered? Commented May 13, 2022 at 11:44
  • No? just preventing someone to flag it without even reading it Commented May 13, 2022 at 11:47
  • 2
    @0stone0 it's not just replacement. In some cases the whole value is changed from string to an object. Overall, I'd say this isn't a straight up question about replacement. It's more of a design problem because right now it's hard to deal with this data and the replacement rules. Moreover, it's unclear what the result should be if the input had SortAs: "hello {{someObject}} world" - is the whole value changed again? Is the object then inserted into the string? What should that produce? Similar problems if you have other non-strings as replacement values. Commented May 13, 2022 at 11:48
  • 1
    @VitorCeolin at the very least I'd personally consider having a different replacement pattern if the whole value should be changed. For example { key: "<someObject>", value: { some:'value' } } will be known from the get-go that it will change the entire value. It might even be { key: "<SortAs>", value: { some:'value' } } (matching the key where the value would be replaced) or maybe even { fullReplace: "SortAs", value: { some:'value' } } (to more directly identify the whole value would be changed). Might even be { key: "{{someObject}}", value: { some:'value' }, full: true } Commented May 13, 2022 at 11:55

2 Answers 2

1

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);

Sign up to request clarification or add additional context in comments.

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.