1

I need to find and replace values in my object when they match a regular expression (e.g. **myVar**); The object that I need to loop through is user defined and structure varies.

Here is an example object, shortened for simplicity.

var testObject = {
    name: "/pricing-setups/{folderId}", 
    method: "POST", 
    endpoint: "/pricing-setups/:folderId", 
    functionName: "create",
    Consumes: null,
    filename: "apicontracts/pricingsetups/PricingSetupServiceProxy.java",
    pathParam: [
        {$$hashKey: "06S",
          key: "folderId",
          value: "**myVar**"}
    ],
    queryParam: [],
    request_payload: "{'title':'EnterAname'}",
    returnList: []
}

This object is passed into a master function that builds a angularjs resource object using the passed in object.

Here is the structure I am using:

function getTestResult(dataSource, options) {
      //input into the service should be api obj and selected environment obj

      //extend the passed object with options if passed
      var opts = $.extend({}, dataSource, options);
      //swap the {param} syntax for :param in opts.endpoint
      opts.endpoint = opts.endpoint.replace(/\}/g, "").replace(/\{/g, ":");

      //replace any defined vars passed in from scenario via scenario.userVar
      opts = replaceUserVars(opts, {"myVar": "C=1000=Corporate"});

    //MORE CODE STUFF
    // ...
    // ...
}

replaceUserVars() is based on the following questions/answers, but my case is different because the structure of the passed in object (var testObject) and the location of the found match will change.

SO... Here is my recursive solution to find the values that match the desired regex

function replaceUserVars(api, uvars) {
      if (!uvars) {
        return api;
      }
      var pattern =  new RegExp("\\*\\*\\w*\\*\\*", "g");//match **myVar**
      //check the api params for a match to regex
      // and if we find a match, replace the string with the userVar[regex match].value

      function warpSpeedAhead(collection) {
        _.find(collection, function (obj) { //find obj in api
          if (obj !== null && typeof(obj) === "object") {
            warpSpeedAhead(obj);
          }
          else {
            if (pattern.test(obj)) { //check the regex
              var sanitVar = obj.replace(/\*/g, ""); //remove the *
              if (uvars[sanitVar]) {
                console.log("found one");
                obj = uvars[sanitVar];
                //should be equivalent to 
                //api.pathParam[0][key] = uvars[sanitVar]; //works in this case ONLY
              }
            }
          }
        });
      }
      warpSpeedAhead(api);

      return api;
    }

This function successfully finds the values that match the regex, however, I can't seem to return the updated object without directly refrencing the structure of the testObject.

Here is a jsfiddle of the code above. http://jsfiddle.net/joshvito/2Lu4oexj/

My goal is to be able to search through the incoming object, find any values that match the regular expression, and change the value to the value defined in userVars (if the object value and userVar key match).

5 Answers 5

6

How about JSON.stringify and replace as string and back to JSON?

JSON.parse(JSON.stringify(testObject).replace(/\*\*([^*]+)\*\*/g,function($0,$1){return uvars[$1]||$0;}))
Sign up to request clarification or add additional context in comments.

3 Comments

This is 4x faster than the recursion method: jsperf.com/recursion-vs-json-parse-json-stringify-x/1
Note: this won't work if you've got a circular json object, i recommend using flatted import { parse, stringify} from 'flatted' and just remove JSON. from your answer
JSON.stringify() will remove any properties that have a value of undefined - if that's okay for your use case then this solution is good.
4

I've made a solution based in your problem, a search and replacer in a complex object... 'll help you?

not change the object reference, only replace in strings...

can see a example in this fiddle... http://jsfiddle.net/Castrolol/gvpnxou0/

/* definition */

function replaceVars(objSource, objReplacer){

    var pattern = replaceVars.pattern;

    if(typeof objSource === "object" ){     
        if(objSource === null) return null;

        if(objSource instanceof Array){
            for(var i = 0; i < objSource.length; i++){
             objSource[i] =  replaceVars(objSource[i], objReplacer); 
            }           
        }else{        
            for(var property in objSource){         
                objSource[property] = replaceVars(objSource[property], objReplacer);            
            }
        }

        return objSource;

    }

    if(typeof objSource === "string"){

        return objSource.replace(pattern, function(finded, varName){
            return varName in objReplacer ? objReplacer[varName] : finded;
        });

    }

    return objSource;

}


 replaceVars.pattern = /\*\*([0-9a-z_$]{1,})\*\*/gi;

you can implement your solution with calling internally this function

1 Comment

This is 4x slower than the JSON.parse(JSON.stringify()) method jsperf.com/recursion-vs-json-parse-json-stringify-x/1
3

I modified Luan Castros solution to my needs. Note that for(key in myObject) is discouraged by linters, because of it also traverses the prototype attributes, which may be unwanted. At the same time, the Object.keys(myObject) will nicely work also on arrays.

function recursiveSubStringReplace (source, pattern, replacement) {

    function recursiveReplace (objSource) {
        if (typeof objSource === 'string') {
            return objSource.replace(pattern, replacement);
        }

        if (typeof objSource === 'object') {
            if (objSource === null) {
                return null;
            }

            Object.keys(objSource).forEach(function (property) {
                objSource[property] = recursiveReplace(objSource[property]);
            });

            return objSource;
        }

    }

    return recursiveReplace(source);
}

Comments

0
  • First: solve the problem of recursively renaming a key. You can use map keys deep
  • Then: write your iteratee so that you return the new key name

const yourObject = { 'a': 1, 'b': 2 }; _.mapKeys(yourObject, function(value, key) { const pattern = /.*/; // whatever you want to match if (key.match(pattern)){ return key + "1234"; // return the new key name } return key; });

Comments

0

For basic data processing we now use object-scan. It's very powerful and makes things a lot cleaner, but it takes a moment to wrap your head around it. Here is how you'd solve your questions

Note that the function mutates the object and returns the count of replaces. If you wanted to just replace the first occurrence, you could set abort to true

// const objectScan = require('object-scan');

const replace = (p, n, data) => objectScan(['**'], {
  rtn: 'count',
  filterFn: ({ value, parent, property }) => {
    if (p.test(value)) {
      parent[property] = n;
      return true;
    }
    return false;
  }
})(data);

const testObject = { name: '/pricing-setups/{folderId}', method: 'POST', endpoint: '/pricing-setups/:folderId', functionName: 'create', Consumes: null, filename: 'apicontracts/pricingsetups/PricingSetupServiceProxy.java', pathParam: [{ $$hashKey: '06S', key: 'folderId', value: '**myVar**' }], queryParam: [], request_payload: "{'title':'EnterAname'}", returnList: [] };

const r = replace(new RegExp('\\*\\*\\w*\\*\\*', 'g'), 'newValue', testObject);
console.log(r);
// => 1

console.log(testObject);
// => { name: '/pricing-setups/{folderId}', method: 'POST', endpoint: '/pricing-setups/:folderId', functionName: 'create', Consumes: null, filename: 'apicontracts/pricingsetups/PricingSetupServiceProxy.java', pathParam: [ { '$$hashKey': '06S', key: 'folderId', value: 'newValue' } ], queryParam: [], request_payload: "{'title':'EnterAname'}", returnList: [] }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/[email protected]"></script>

Disclaimer: I'm the author of object-scan

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.