Reputation: 93
I have an json object(firstObj) It can be nested and I have an second object containing key/value pair. I want to replace the second object's value by first one by matching value and do it's operation.
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 }
]
};
my second object
let secondObj = {
payment_amt: 100,
payment_cur: 'USD',
request_id: '123ASD',
ship_country: [
{ code: 'USA', Title: 'America' },
{ code: 'UK', Title: 'England' }
]
};
I want something like this
{
amount_money: {
amount: 10
currency: 'USD'
},
source_id: '123ASD',
ship: [ {America: 'USA'}, {England: 'UK'}]
}
Really appreciate your kind help, Thank you!
Upvotes: 3
Views: 2195
Reputation: 2171
To address the updated question
.as-console-wrapper {max-height: 100% !important; top: 0}
<script type="module">
import objectScan from 'https://cdn.jsdelivr.net/npm/[email protected]/lib/index.min.js';
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' } ] }
</script>
Disclaimer: I'm the author of object-scan
Old Answer (pre edit of question)
Here is a generic solution using object-scan.
.as-console-wrapper {max-height: 100% !important; top: 0}
<script type="module">
import objectScan from 'https://cdn.jsdelivr.net/npm/[email protected]/lib/index.min.js';
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' }
</script>
Disclaimer: I'm the author of object-scan
Important:
Edit (as requested in comment)
How this works:
apply
is a helper function, taking the input and an operation. We use a regex to determine the operator and then apply it against the inputcompile
is the core logic. We target the field with **.*.mapped_field
, where the single *
ensures that we are targeting an object and not an arrayfilterFn
we check if the value is known in context
(which is values
). If that is the case update the the value using the grandparent (gparent
) and grandproperty (gproperty
). All the parameters of filterFn
are relative to the target (in this case mapped_field
)count
is just the return value (in this case how often true
was returned from filterFn
This is all documented n great detail in object-scan. Have a look there. It might also greatly help your understanding to put in come log statements!
Upvotes: 1
Reputation: 181
If you don't know how many nested nodes are there. There is a more overall solution using recursion.
Here is the solution:
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"}}}}
I don't know how many nested in your firstObj. But this code below can solve the example which you give.
I replace the second object's value by first one by matching value.
If operation not equal null, use eval() function evaluates JavaScript code represented as a string.
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' } }
Note: If firstObj have only one key like amount_money, you can make your code precise by using only one loop.
Upvotes: 1