通过键查找获取值并替换为嵌套 json 对象中的第二个 json 值

Find by key get value and replace by second json value in nested json object

我有一个 json 对象 (firstObj) 它可以嵌套,我有第二个对象包含 key/value 对。我想通过匹配值将第二个对象的值替换为第一个对象的值并进行操作。

let firstObj = {
    amount_money: {
       amount: {
           mapped_field: 'payment_amt',
           operation: '/10'
       },
       currency: {
          mapped_field: 'payment_cur',
          operation: null
       }
    },
   source_id: {
          mapped_field: 'request_id',
          operation: null
   },
  ship: [ 
            { mapped_field: 'ship_country[0]', operation: null },
            { mapped_field: 'ship_country[1]', operation: null } 
        ]
 };
       

我的第二个对象

let secondObj = {
     payment_amt: 100,
     payment_cur: 'USD',
     request_id: '123ASD',
     ship_country: [ 
                     { code: 'USA', Title: 'America' }, 
                     { code: 'UK', Title: 'England' } 
                   ] 
    };

我想要这样的东西

{
  amount_money: {
     amount: 10
     currency: 'USD'
  },
  source_id: '123ASD',
  ship: [ {America: 'USA'}, {England: 'UK'}]
}

非常感谢您的帮助, 谢谢!

更新

如果你不知道有多少个嵌套节点。使用 recursion.

有一个更全面的解决方案

解决方法如下:

const firstObj = {
  source_name: {
    mapped_field: 'request_name',
    operation: null,
  },
  amount_money: {
    amount: {
      mapped_field: 'payment_amt',
      operation: '/10',
    },
    currency: {
      mapped_field: 'payment_cur',
      operation: null,
    },
  },
  source_id: {
    mapped_field: 'request_id',
    operation: null,
  },
  nested: {
    nested: {
      nested: {
        nested: {
          mapped_field: 'mapping_nested',
          operation: null,
        },
      },
    },
  },
};

let secondObj = {
  payment_amt: 100,
  payment_cur: 'USD',
  request_id: '123ASD',
  request_name: 'Dollar',
  mapping_nested: 'Hello',
};

const procedure = (firstObj, parentObj = {}, nestedObj = {}) => {
  for (const [key, value] of Object.entries(firstObj)) {
    if (value.hasOwnProperty('mapped_field') && value.hasOwnProperty('operation')) {
      nestedObj[key] = value.operation
        ? eval(secondObj[value.mapped_field] + value.operation)
        : secondObj[value.mapped_field];
    } else {
      nestedObj[key] = {};
      procedure(value, parentObj, nestedObj[key]);
    }
  }
  return (parentObj = { ...nestedObj });
};

const result = procedure(firstObj);
console.log(JSON.stringify(result));
// {"source_name":"Dollar","amount_money":{"amount":10,"currency":"USD"},"source_id":"123ASD","nested":{"nested":{"nested":{"nested":"Hello"}}}}

旧答案

我不知道你的 firstObj 嵌套了多少。但是下面这段代码可以解决你给出的例子。

我通过匹配值将第二个对象的值替换为第一个对象的值。

如果 operation 不等于 null,使用 eval() 函数计算 JavaScript 表示为字符串的代码。

const result = {};
for (const [parentKey, parentValue] of Object.entries(firstObj)) {
  result[parentKey] = {};
  for (const [childKey, childValue] of Object.entries(parentValue)) {
    result[parentKey][childKey] = childValue.operation
      ? eval(secondObj[childValue.mapped_field] + childValue.operation)
      : secondObj[childValue.mapped_field];
  }
}
console.log(result); //{ amount_money: { amount: 10, currency: 'USD' } }

注意:如果firstObj只有一个键,如amount_money,你可以只使用一个来使你的代码更精确循环。

解决更新后的问题

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

const myTemplate = { amount_money: { amount: { mapped_field: 'payment_amt', operation: '/10' }, currency: { mapped_field: 'payment_cur', operation: null } }, source_id: { mapped_field: 'request_id', operation: null }, ship: [{ mapped_field: 'ship_country[0]', operation: null }, { mapped_field: 'ship_country[1]', operation: null }] };
const myVariables = { payment_amt: 100, payment_cur: 'USD', request_id: '123ASD', ship_country: [{ code: 'USA', Title: 'America' }, { code: 'UK', Title: 'England' }] };

const apply = (input, operation) => {
  if (operation === null) {
    return input;
  }
  const action = /(?<op>[/+])(?<v>\d+)/g.exec(operation);
  if (action === null) {
    throw new Error(`Unknown operation: ${operation}`);
  }
  if (action.groups.op === '/') {
    return input / action.groups.v;
  }
  // action.groups.op === '+'
  return input + action.groups.v;
};

const compile = objectScan(['**.mapped_field'], {
  rtn: 'count',
  filterFn: ({ gparent, gproperty, parent, value, context }) => {
    const data = objectScan([value], { rtn: 'value', abort: true })(context);
    if (data === undefined) {
      return false;
    }
    gparent[gproperty] = apply(data, parent.operation);
    return true;
  }
});

console.log(compile(myTemplate, myVariables));
// => 5

console.log(myTemplate);
// => { amount_money: { amount: 10, currency: 'USD' }, source_id: '123ASD', ship: [ { code: 'USA', Title: 'America' }, { code: 'UK', Title: 'England' } ] }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan@16.0.2"></script>

免责声明:我是object-scan

的作者

旧答案(问题的预编辑)

这是使用 object-scan 的通用解决方案。

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

const template = { amount_money: { amount: { mapped_field: 'payment_amt', operation: '/10' }, currency: { mapped_field: 'payment_cur', operation: null } }, source_id: { mapped_field: 'request_id', operation: null } }
const values = { payment_amt: 100, payment_cur: 'USD', request_id: '123ASD' };

const apply = (input, operation) => {
  if (operation === null) {
    return input;
  }
  const action = /(?<op>[/+])(?<v>\d+)/g.exec(operation);
  if (action === null) {
    throw new Error(`Unknown operation: ${operation}`);
  }
  if (action.groups.op === '/') {
    return input / action.groups.v;
  }
  // action.groups.op === '+'
  return input + action.groups.v;
};

const compile = objectScan(['**.*.mapped_field'], {
  rtn: 'count',
  filterFn: ({ gparent, gproperty, parent, value, context }) => {
    if (value in context) {
      gparent[gproperty] = apply(context[value], parent.operation);
      return true;
    }
    return false;
  }
});

console.log(compile(template, values));
// => 3

console.log(template);
// => { amount_money: { amount: 10, currency: 'USD' }, source_id: '123ASD' }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan@16.0.2"></script>

免责声明:我是object-scan

的作者

重要:

  • 请注意,这确实会修改模板 object。如果不需要,您可以 clone 它。
  • 您可以使用 eval,但它被认为是危险的。这需要做更多的工作,但您可能应该自己实现所有支持的操作,因为我已经在“应用”功能中开始了。
  • 您也许可以使用 safe-eval,但我没有使用它的经验,也不知道它有多安全。如果您担心任何类型的注入攻击,请不要使用它。

编辑(按评论要求)

这是如何工作的:

  • apply 是一个辅助函数,接受输入和操作。我们使用正则表达式来确定运算符,然后将其应用于输入
  • compile是核心逻辑。我们使用 **.*.mapped_field 定位字段,其中单个 * 确保我们定位的是 object 而不是数组
  • filterFn 中,我们检查该值是否在 context 中已知(即 values)。如果是这种情况,请使用祖父母 (gparent) 和祖父母 (gproperty) 更新值。 filterFn的所有参数都是相对于目标的(本例mapped_field
  • count 只是 return 值(在这种情况下,truefilterFn
  • 中 return 的频率

object-scan 中详细记录了所有这些内容。看看那里。加入come log语句也可能对您的理解有很大帮助!