1

I've got interesting question. Imagine, we have a string like:

'i need this {client.car.model} exactly at {client.order.time} today'

How to replace such complex fields with object values? I don't know, how many levels: client.car.door.left... etc

Of course, i can extract this via regex in loop.

const re = /{(.+?)}/gi;

let regex = re.exec(s);

But after that - how to replace all fields & sub-fields?

3
  • 1
    Please give an example of a field or subfield that would not be replaced by the solution you have suggested. Commented May 18, 2021 at 12:20
  • Assuming you have the client object, you should build a small parser that given an expression such as {client.car.model} the parser will return: client[car][model]. Regex is probably NOT the right way to go! Commented May 18, 2021 at 12:25
  • i don't like my current solution (next comment) Commented May 18, 2021 at 12:33

2 Answers 2

1

I just don't like my currect solution, but it works...

function objReplace(string, object) {
  const re = /{(.+?)}/gi;
  let res = string;
  let regex = re.exec(string);

  while (regex && regex[1]) {
    const ar = regex[1].split('.');
    let obj = object;
    ar.forEach((key) => {
      obj = obj[key];
    });

    obj = obj || '';
    res = res.replace(regex[0], obj);
    regex = re.exec(string);
  }
  return res.replace(/ +/g, ' ');
}
Sign up to request clarification or add additional context in comments.

Comments

0

So, you've already correctly identified HOW to get the pattern, so your real question is "how can I take a string like 'client.order.time' and get a value for it?"

With a recursive function, it is surprisingly straightforward. Say we have an object like below:

const data = {
  client: {
    order: {
      time: '12:00PM'
    }
  }
};

We can create a function that takes an object and a string, and recursively loops through each part until it either a) gets through all the parts and finds the value or b) finds an unknown part and returns undefined.

const data = {
  client: {
    order: {
      time: '12:00PM'
    }
  }
};

const getValueFromPath = (obj, path, delimiter = '.') => {
  // path or obj are falsy, just return obj
  // you could tweak this bit to do something else if you want
  // obj === undefined might be from our recursive call, so keep that in mind if you tweak
  if (!path || !obj) return obj;

  const parts = path.split(delimiter);
  const next = parts.shift(); // get the first part, parts now has all the other parts
  
  // If !parts.length (parts.length === 0), we're on the last part, so we'll return.
  if (!parts.length) return obj[next];
  
  // Otherwise, recurse with the rest of the parts.
  return getValueFromPath(obj[next], parts.join(delimiter), delimiter);
};

const result = getValueFromPath(data, 'client.order.time');
console.log(result);

const noResult = getValueFromPath(data, 'path.does.not.exist');
console.log(noResult);

If you don't like re-join()-ing the parts every time, you could have a second version of this method which takes an array of parts instead of a path string, but I find the extra code isn't worth the (very minimal) efficiency gains.

Now, circling back to your original question, simply use the replace() method on the string, using a replacer method as the second argument. The 2nd argument of that replacer method is the pattern, so we just feed that to our new method.

const str = 
'i need this {client.car.model} exactly at {client.order.time} today';
const data = {
  client: {
    car: {
      model: 'Truck'
    },
    order: {
      time: '12:00PM'
    }
  }
};

const getValueFromPath = (obj, path, delimiter = '.') => {
  // path or obj are falsy, just return obj
  // you could tweak this bit to do something else if you want
  // obj === undefined might be from our recursive call, so keep that in mind if you tweak
  if (!path || !obj) return obj;

  const parts = path.split(delimiter);
  const next = parts.shift(); // get the first part, parts now has all the other parts
  
  // If !parts.length (parts.length === 0), we're on the last part, so we'll return.
  if (!parts.length) return obj[next];
  
  // Otherwise, recurse with the rest of the parts.
  return getValueFromPath(obj[next], parts.join(delimiter), delimiter);
};

const result = str.replace(/{(.+?)}/gi, (_, path) => getValueFromPath(data, path));
console.log(result);

2 Comments

And what about arrays? Can we parse strings like 'client.cars[0].model' ?
You can. You would just need to add code to check if the part looks like something[#] and then handle that accordingly, before the final return in there.

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.