рекурсивный поиск и замена в многомерном объекте javascript

Мне нужно найти и заменить значения в моем объекте, когда они соответствуют регулярному выражению (например, **myVar**); Объект, который мне нужно прокрутить, определяется пользователем, а структура меняется.

Вот пример объекта, сокращенный для простоты.

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: []
}

Этот объект передается в основную функцию, которая создает объект ресурса angularjs, используя переданный объект.

Вот структура, которую я использую:

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() основан на следующих вопросах/ответах, но мой случай отличается, потому что изменится структура переданного объекта (var testObject) и местоположение найденного совпадения.

ТАК... Вот мое рекурсивное решение, чтобы найти значения, соответствующие желаемому регулярному выражению

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

Эта функция успешно находит значения, соответствующие регулярному выражению, однако я не могу вернуть обновленный объект без прямого обращения к структуре testObject.

Вот jsfiddle кода выше. http://jsfiddle.net/joshvito/2Lu4oexj/3/

Моя цель состоит в том, чтобы иметь возможность искать во входящем объекте, находить любые значения, соответствующие регулярному выражению, и изменять значение на значение, определенное в userVars (если значение объекта и ключ userVar совпадают).


person joshvito    schedule 06.04.2015    source источник


Ответы (5)


Я сделал решение, основанное на вашей проблеме, поиск и замена в сложном объекте... вам поможет?

не изменять ссылку на объект, только заменять в строках...

можно увидеть пример в этой скрипке... 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;

вы можете реализовать свое решение с помощью внутреннего вызова этой функции

person Luan Castro    schedule 06.04.2015
comment
Это в 4 раза медленнее, чем метод JSON.parse(JSON.stringify()) jsperf.com/recursion-vs-json-parse-json-stringify-x/1 - person jeznag; 13.08.2018

Как насчет JSON.stringify и замены как строки и обратно в JSON?

JSON.parse(JSON.stringify(testObject).replace(/\*\*([^*]+)\*\*/g,function($0,$1){return uvars[$1]||$0;}))
person YOU    schedule 06.04.2015
comment
Это в 4 раза быстрее, чем метод рекурсии: jsperf.com/recursion -vs-json-parse-json-stringify-x/1 - person jeznag; 13.08.2018
comment
Примечание: это не сработает, если у вас есть круговой объект json, я рекомендую использовать flatted import { parse, stringify} from 'flatted' и просто удалить JSON. из вашего ответа. - person Shannon Hochkins; 17.02.2021

Я модифицировал решение Luan Castros под свои нужды. Обратите внимание, что линтеры не рекомендуют for(key in myObject), поскольку он также проходит через атрибуты прототипа, что может быть нежелательным. В то же время Object.keys(myObject) прекрасно работает и с массивами.

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);
}
person Markko Paas    schedule 16.09.2015

  • Первое: решить проблему рекурсивного переименования ключа. Вы можете использовать сопоставлять ключи глубоко
  • Затем: напишите свою итерацию, чтобы вы вернули новое имя ключа

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

person Flavien Volken    schedule 10.05.2018

Для базовой обработки данных мы теперь используем object-scan. Он очень мощный и делает вещи намного чище, но требуется время, чтобы осознать это. Вот как бы вы решили свои вопросы

Обратите внимание, что функция изменяет объект и возвращает количество замен. Если вы хотите просто заменить первое вхождение, вы можете установить abort в 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>

Отказ от ответственности: я являюсь автором object-scan

person vincent    schedule 25.10.2020