Jack
Jack

Reputation: 33

How to write a function that takes a string and object and interpolates that object in the string?

Hi everyone I need to write a function that takes a string and object and interpolates that object in the string so something like this

// interpolate("Its {weather} outside", {weather: 'damn Hot'})
// returns 'It's damn hot outside'


// interpolate( "Hi my name is {name}", {name: 'John'});
// returns 'Hi my name is John'

It should also no matter how deep the object goes so a case like this

// interpolate("Hi my name is {contact.name}", {contact: {name: 'john'}});

Im a little stuck and at first I tried splitting up the string into an array then trying to fit the object value in the array then join that back together but that has not worked for all test cases, can someone help me write this function and explain their solution please? Thankyou

so I tried something like this but does not really work for all test cases, this is a template literal but the function should just work giving those values as arguments on its own, otherwise I'm pretty stuck . .

function interpolate(strings, ...keys) {
  return (function(...values) {
    var dict = values[values.length - 1] || {};
    var result = [strings[0]];
    keys.forEach(function(key, i) {
      var value = Number.isInteger(key) ? values[key] : dict[key];
      result.push(value, strings[i + 1]);
    });
    return result.join('');
  });
}

var t1Closure = interpolate`${0}${1}${0}!`;
t1Closure('Y', 'A');  // "YAY!" 
var t2Closure = interpolate`${0} ${'foo'}!`;
console.log(t2Closure('Hello', {foo: 'World'}));  // "Hello World!"

ok I'm getting closer so I separated the problem into two functions and need to combine them, the only thing is I'm not sure how to get the last use case to work without template literals

var something = "something";
var sub = function(str) {
   return str.replace(/#\{(.*?)\}/g,
    function(whole, expr) {
      return eval(expr)
    })
}

console.log(sub("hello #{something}"));



var objectValue = function(str, obj){

     for(key in obj) {
       if(obj.hasOwnProperty(key)) {
          var value = obj[key];
          return str + value;
      }
   }
}


console.log(objectValue("hi my name is ", {contact: {name: 'john'}}));

Upvotes: 3

Views: 452

Answers (5)

Relu Mesaros
Relu Mesaros

Reputation: 5038

Here you go:

'use strict';
function interpolate(str, obj) {
  for (let prop in obj) {
    if (obj.hasOwnProperty(prop)) {
        str = str.replace(new RegExp(`{${prop}}`, 'g'), obj[prop]);
    }
  }
  return str;
}
console.log(interpolate("Its {weather} outside", {weather: 'damn Hot'}));
// Its damn Hot outside

Upvotes: 1

Zabavsky
Zabavsky

Reputation: 13640

A little late to the party, but in case you cannot (don't want to) use any external libraries or ES6, here is the idea:

function getMatches(s) {
    var regExp = /{([^}]*)}/g,
        matches = [],
        match;

    while ((match = regExp.exec(s)) != null) {
        matches.push(match[1]);
    }
    return matches;
}

function objValue(obj, i) {
    return obj[i];
}

function interpolate(s, obj) {
    var matches = getMatches(s),
        result = s;

    matches.forEach(function (match) {
        var props = match.split('.'),
            value = props.reduce(objValue, obj) || '';

        result = result.replace('{' + match + '}', value);
    });
    return result;
}

Usage

interpolate("Its {weather} outside", { weather: 'damn Hot' });

JSFiddle.

Upvotes: 0

trincot
trincot

Reputation: 350310

Using the dreaded eval

If you control the string that you pass and consider it safe, you can use eval:

function interpolate (str, obj)  {
    return str.replace(/\{(.*?)\}/g, function (_, ref) {
        return eval('obj.' + ref);
    });
}

var output = interpolate("Its {weather} outside", {weather: 'damn Hot'});
console.log(output);

output = interpolate("Hi my name is {contact.name}", {contact: {name: 'john'}});
console.log(output);

output = interpolate("The highest bid is {rank[0].bid}", {rank: [{bid: 900}, {bid:600}]});
console.log(output);

Be aware that if given a string like '{x;alert("hi")}', the above function would execute that alert, or any code that is put instead. So this is not a good solution if the string is provided (or can be altered) by the user.

Template Literals

It does not follow your function descriptor, but template literals already offer the functionality you are looking for:

var weather = 'damn Hot';
var output = `It's ${weather} outside`;
console.log(output);

var contact = {name: 'john'};
var output = `Hi my name is ${contact.name}`;
console.log(output);

Upvotes: 3

micah
micah

Reputation: 8096

Or lodash templates. Lodash already has a lot of handy features you end up using a lot of in my opinion.

var str = _.template('Its <%= weather %> outside')({weather: 'damn Hot'});

// If you wanted to use the {{:var}} syntax.
_.templateSettings.interpolate = /{{:([\s\S]+?)}}/g;
var str = _.template('Its {{:weather}} outside')({weather: 'damn Hot'});

Upvotes: 0

Grim
Grim

Reputation: 1996

Try renderjs.

$("<p>Its {{:weather}} outside</p>").render({weather: 'damn Hot'}) 
-> <p>Its damn Hot outside</p>

Upvotes: 0

Related Questions