5
/** Supplant **/
String.prototype.supplant = function(o) {
    return this.replace (/{([^{}]*)}/g,
        function (a, b) {
            var r = o[b];
            return typeof r === 'string' || typeof r === 'number' ? r : a;
        }
    );
};

Crockford is no doubt a JavaScript Grand Wizard, but his prototype is lacking when it comes to multiple level objects.

I would like this function to cover multiple level object replacement such as '{post.detailed}' could anyone help me with a revised version of supplant?

Tyler Buchea
  • 1,642
  • 3
  • 17
  • 25
  • This should help: http://stackoverflow.com/questions/4343028/in-javascript-test-for-property-deeply-nested-in-object-graph – Felix Kling Oct 16 '12 at 08:33

3 Answers3

5

That shouldn't be too difficult. Use this replace function instead:

function (a, b) {
    var r = o,
        parts = b.split(".");
    for (var i=0; r && i<parts.length; i++)
        r = r[parts[i]];
    return typeof r === 'string' || typeof r === 'number' ? r : a;
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
3

I personally hate it when people stuff their own rubbish on the native types in JavaScript. If I were to write it I would do the following... But why no love for boolean?

function supplant(str, data) {
    return str.replace(/{([^{}]*)}/g, function (a, b) {

        // Split the variable into its dot notation parts
        var p = b.split(/\./);

        // The c variable becomes our cursor that will traverse the object
        var c = data;

        // Loop over the steps in the dot notation path
        for(var i = 0; i < p.length; ++i) {

            // If the key doesn't exist in the object do not process
            // mirrors how the function worked for bad values
            if(c[p[i]] == null)
                return a;

            // Move the cursor up to the next step
            c = c[p[i]];
        }

        // If the data is a string or number return it otherwise do
        // not process, return the value it was, i.e. {x}
        return typeof c === 'string' || typeof c === 'number' ? c : a;
    });
};

It doesn't support arrays btw, you would need to do some additional stuff to support that.

Stuart Wakefield
  • 6,294
  • 24
  • 35
  • 1
    Why do you think it doesn't support arrays? Just use `supplant("{myArray.0}", {myArray:["foo"]})` – Bergi Oct 16 '12 at 08:59
  • Good call! of course it will do type coercion "0" => 0... Duh. – Stuart Wakefield Oct 16 '12 at 09:04
  • Actually, it doesn't - property names are always strings; and that's also true for Array objects. – Bergi Oct 16 '12 at 09:10
  • Incorrect. Arrays are numerically indexed, you access items like `arr[0]` not `arr["0"]`, JavaScript is just clever enough to work out what you mean should you use the latter. – Stuart Wakefield Oct 16 '12 at 09:57
  • I take it back you are quite correct! Arrays give special treatment and considers a string property `P` an array index if `ToString(ToUint32(P))` is equal to `P` and `ToUint32(P)` is not equal to `2^32−1`. http://ecma-international.org/ecma-262/5.1/#sec-15.4. So in fact it is reverse `arr[0]` is coerced to `arr["0"]` not the other way around. – Stuart Wakefield Oct 16 '12 at 10:06
  • This is great. It supports multilevel objects. – Simon Sep 19 '16 at 09:10
  • @StuartWakefield Its failing when object kye has a value of `null`, but the key exists `{ a: null, b: 'Hello'}`, how can I make it work with null value to print empty string – Saqueib Mar 02 '20 at 07:17
  • First, replace `c[p[i]] == null` with `!c.hasOwnProperty(p[i])` so that the null value is not treated as a missing key. Lastly, change the return statement: `return typeof c === 'string' || typeof c === 'number' ? c : a;` so that it performs the special behaviour for null, i.e. `return typeof c === 'string' || typeof c === 'number' ? c : c == null ? '' : a;` – Stuart Wakefield Mar 03 '20 at 08:15
2

@Bergi method w/ support to boolean:

function (a, b) {
    var r = o,
        parts = b.split(".");
    for (var i=0; r && i<parts.length; i++)
        r = r[parts[i]];
    return typeof r === 'string' || typeof r === 'number' || typeof r === 'boolean' ? r : a;
}

Original Crockford's Supplant method w/ support to boolean:

if (!String.prototype.supplant) {
    String.prototype.supplant = function (o) {
        return this.replace(/{([^{}]*)}/g,
            function (a, b) {
                var r = o[b];
                return typeof r === 'string' || typeof r === 'number' || typeof r === 'boolean' ? r : a;
            }
        );
    };
}

Best of luck!

https://gist.github.com/fsschmitt/b48db17397499282ff8c36d73a36a8af

fsschmitt
  • 599
  • 5
  • 10