JSON conversion for outer key value pair - javascript

I have one JSON in following structure:
data = {
"a" : "1",
"b" : {
"c" : 2
}
}
I want to convert it to following structure:
data = {
a = "1",
b = {
"c":2
}
}
I have tried to use map, but not getting correct way to do the conversion.

Try this:
const data = {
a: "1",
b: {
c: 2
}
};
const convertJson = data => {
const _data = [];
Object.keys(data).forEach(key => {
_data.push(`${key}=${JSON.stringify(data[key])}`);
});
return `{${_data.join(",")}}`;
};
console.log(convertJson(data));

Related

How to add dynamic keys with nested level from the array in javascript

Hi I want to create new object on the basis of path array. It will be dynamic. I tried with reduce but it is not giving correct result.
const obj = {
"e": [{
"name": "moviename",
"path": ["a"]
},
{
"name": "hero",
"path": ["a", "b"]
},
{
"name": "desc",
"path": ["c"]
},
{
"name": "udf",
"path": []
}
]
}
// this is what i want this object to be created programatically. after parsing above obj.
const output = {
"a": {
"moviename": "",
"b": {
"hero": ""
}
},
"c" : {
"desc": ""
},
"udf": ""
}
const payload = {};
obj.e.forEach((item) => {
if (item.path.length === 0) {
payload = {
...payload,
[item.name]: ''
};
} else {
item.path.reduce((o, s, index) => {
if ((index + 1) === item.path.length) {
return o[s] = {
[item.name]: ''
};
}
return o[s] = {};
}, payload);
}
});
console.log(payload);
You can use simple for loops -- reduce would also work (see further down), but I think the overhead of a callback is not worth it:
function convert(arr) {
const output = {};
for (const {name, path} of arr) {
let node = output;
for (let prop of path) {
node = (node[prop] ??= {});
}
node[name] = "";
}
return output;
}
const obj = {"e": [{"name": "moviename","path": ["a"]},{"name": "hero","path": ["a", "b"]},{"name": "desc","path": ["c"]},{"name": "udf","path": []}]};
console.log(convert(obj.e));
With reduce:
Using reduce it would translate to this:
function convert(arr) {
return arr.reduce((output, {name, path}) => {
let node = output;
for (let prop of path) {
node = (node[prop] ??= {});
}
node[name] = "";
return output;
}, {});
}
const obj = {"e": [{"name": "moviename","path": ["a"]},{"name": "hero","path": ["a", "b"]},{"name": "desc","path": ["c"]},{"name": "udf","path": []}]};
console.log(convert(obj.e));
With double reduce:
If the inner loop is also done through reduce, then:
function convert(arr) {
return arr.reduce((output, {name, path}) => {
path.reduce((node, prop) => node[prop] ??= {}, output)[name] = "";
return output;
}, {});
}
const obj = {"e": [{"name": "moviename","path": ["a"]},{"name": "hero","path": ["a", "b"]},{"name": "desc","path": ["c"]},{"name": "udf","path": []}]};
console.log(convert(obj.e));
The logical nullish assignment operator
If your environment has no support for ??= then use one of the following alternatives:
node[prop] ||= {}
(node[prop] = node[prop] ?? {})
(node[prop] = node[prop] || {})
Some comments on your code
As this function builds the object from scratch, it is not really necessary to treat intermediate versions of the object as immutable -- as your code attempts to do at least in the case of path.length == 0: just keep extending the object through mutation.
return o[s] = {}; is destructive: if the property was already created from a previously processed path, then this will overwrite whatever was already assigned to o[s].

Javascript conditionally adding the nested object using map() operator

I have a below JSON,
var original = {
"todos": [
{
"accountNo": "50190000",
"name": "Sarkar",
"vpainfo": [
{
"vpa": "log#bda",
"mccCode": "0000"
}
]
}
]
}
And am trying to add new data inside the nested array i.e., "vpainfo". I have tried using the below code and able to adding the new values inside "vpainfo".
var newdata = {"vpa":"first#bda","mccCode":"1111"};
var newObj =
Object.assign({}, original,
{
todos: original.todos.map(todoInfo=>(todoInfo.accountNo=="50190000")?[
...todoInfo.vpainfo,
newdata
]: todoInfo)
});
And the resulted object is,
{"todos":[[{"vpa":"log#bda","mccCode":"0000"},{"vpa":"first#bda","mccCode":"1111"}]]}
But few of the key and values(accountNo and name) are getting missed, how do we get the full object with the latest updated values?
You only return the array, not the actual object, hence the error.
var original = {
"todos": [
{
"accountNo": "50190000",
"name": "Sarkar",
"vpainfo": [
{
"vpa": "log#bda",
"mccCode": "0000"
}
]
}
]
}
const newdata = {"vpa":"first#bda","mccCode":"1111"};
const newObj = Object.assign({}, original,
{
todos: original.todos.map(todoInfo=>{
if(todoInfo.accountNo=="50190000"){
return {
...todoInfo,
vpainfo: [...todoInfo.vpainfo, newdata]
}
}
return todoInfo
})
});
console.log(newObj)
All those spread operators seem a little excessive...
If all you wanna do is add newdata to that existing array, then do that:
var original = {
"todos": [{
"accountNo": "50190000",
"name": "Sarkar",
"vpainfo": [{
"vpa": "log#bda",
"mccCode": "0000"
}]
}]
};
const newdata = {
"vpa": "first#bda",
"mccCode": "1111"
};
// Find the correct account.
const account = original.todos.filter(t => t.accountNo === '50190000')[0];
if (account) {
account.vpainfo.push(newdata);
}
console.log(original);

How to iterate through complex Json object and do something on each property which equals a certain value

I got the following problem,
I need to iterate through a big Json object ( child nodes consist of array's, strings and objects with at least 4-5 layers of depth in terms of nested properties ).
In some parts across the big Json file there is a specific object structure, it has a property named "erpCode". I need to scan the Json and find all the objects with that property, take the value use that code to ask a different API for details and once I get the details insert them into the object with the current 'erpCode'.
Just to clarify, in my case the parent node property name in the Json always equals the value in 'typeSysname' field which located on the same 'level' as the erpCode property.
A simple example :
{
"cars": [
{
"name": "X222",
"carType": {
"erpCode": "skoda",
"value": null,
"typeSysName": "carType"
}
}
],
"model": {
"year": 1999,
"details": {
"erpCode": "112"
"value": null,
"typeSysName": "details"
}
}
}
In this example I need to find 2 properties get the values skoda and 112 out of them and get the value and description data from a different API and set it into this Json in the right location.
P.S. Any chance there is a good npm package which can help me with that?
Edit:
I got a solution in C# from a few months ago which runs in a generic way on the Json and handles the complexity of the structure in a generic way.
But I now need to convert this into Javascript and I am a bit lost.
public static string TranslateDocErpCodes(string jsonString, string topRetailerSysName)
{
try
{
var doc = JObject.Parse(jsonString);
var erpCodeList = doc.SelectTokens("$..erpCode").ToList();
foreach (var erpCodeJToken in erpCodeList)
{
var value = erpCodeJToken?.Value<string>();
var erpCodeParent = erpCodeJToken?.Parent.Parent;
var erpCodeProperty = erpCodeParent?.Path.Split(".").Last();
var result =
_dataService.GetLovFromErpCode(topRetailerSysName, erpCodeProperty, value);
if (result == null)//reset lov obj
{
if (erpCodeParent?.Parent is JProperty prop)
prop.Value = JObject.FromObject(new LovObject { ErpCode = value });
}
else//set lov obj
{
result.ErpCode = value;
if (erpCodeParent?.Parent is JProperty prop)
prop.Value = JObject.FromObject(result);
}
}
return JsonConvert.SerializeObject(doc);
}
catch (Exception e)
{
throw new Exception("ErpConvert.TranslateDocErpCodes() : " + e);
}
}
mb something like;
function processObject(jsonData) {
for (prop in jsonData) {
if (jsonData.hasOwnProperty(prop)) {
// We get our prop
if (prop === 'code') {
let codeValue = jsonData[prop]
doSomeAsync(codeValue)
.then(response => {
jsonData[prop] = response;
})
}
let curValue = jsonData[prop];
if (Array.isArray(curValue)) {
// Loop through the array, if array element is an object, call processObject recursively.
processArray(curValue);
} else if (typeof curValue === 'object') {
processObject(curValue);
}
}
}
}
I took the answer from Aravindh as a starting point and managed to reach what seems to be a complete solution.
I will share it here,
async function convertErpCodes(jsonData, orgName, parentPropertyName){
for (let prop in jsonData) {
if (jsonData.hasOwnProperty(prop)) {
if (prop === 'erpCode') {
const erpCodeValue = jsonData[prop]
const req = {"query": {"erpCode": erpCodeValue, "orgName": orgName, "typeSysName": parentPropertyName}};
const result = await viewLookupErpService.findOne(req);
if(result)
return result;
}
const curValue = jsonData[prop];
if (Array.isArray(curValue)) {
for(let i in curValue){
const res = await convertErpCodes(curValue[i], orgName, prop);
}
} else if (curValue && typeof curValue === 'object') {
const response = await convertErpCodes(curValue, orgName, prop);
if(response){
jsonData[prop] = response;
}
}
}
}
}
P.S.
I set up the values only if I get a response from the third party API ( this is the reason for the result and response logic in the recursion.
I'd use object-scan and lodash.set in combination
// const objectScan = require('object-scan');
// const lodash = require('lodash');
const stats = { cars: [{ name: 'X222', carType: { erpCode: 'skoda', value: null, typeSysName: 'carType' } }], model: { year: 1999, details: { erpCode: '112', value: null, typeSysName: 'details' } } };
const entries = objectScan(['**.erpCode'], { rtn: 'entry' })(stats);
console.log(entries);
// => [ [ [ 'model', 'details', 'erpCode' ], '112' ], [ [ 'cars', 0, 'carType', 'erpCode' ], 'skoda' ] ]
// where you would query the external api and place results in entries
entries[0][1] = 'foo';
entries[1][1] = 'bar';
entries.forEach(([k, v]) => lodash.set(stats, k, v));
console.log(stats);
// => { cars: [ { name: 'X222', carType: { erpCode: 'bar', value: null, typeSysName: 'carType' } } ], model: { year: 1999, details: { erpCode: 'foo', value: null, typeSysName: 'details' } } }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
<script src="https://bundle.run/lodash#4.17.20"></script>
Disclaimer: I'm the author of object-scan

Add item into array instead of overriding the current value with the new value

I'm trying to figure out how to add new items into array instead of overriding the current value with the new value. I'm using .push() which should add the item every time it maps through the array. Any Ideas?
const searchChips = [
{value: "string"}, {value: "test"}
];
const query = {
bool: {
filter: []
}
};
const searchQuery = {
query_string: {
query: ""
}
};
searchChips.map(chip => {
console.log(chip);
const key = "query";
searchQuery.query_string[key] = chip.value;
query.bool.filter.push(searchQuery);
});
console.log(query);
You are inserting the same query since you are dealing with the same exact reference to the searchQuery. Instead of this try having it as a function which returns an object:
const searchChips = [{
value: "string"
}, {
value: "test"
}];
const query = {
bool: {
filter: []
}
};
let sq = (query) => ({
query_string: {query}
});
searchChips.map(chip => query.bool.filter.push(sq(chip.value)));
console.log(query);
This will return to you the 2 filters each with different values for query_string since now the function will return an entirely new object instead of you dealing with the same reference.
The problem seems to be that you are pushing into query.bool.filter outside the .map() function. Try this.
const searchChips = [{ value: "string" }, { value: "test" }];
const query = {
bool: {
filter: []
}
};
searchChips.forEach(chip => {
const key = "query";
const searchQuery = {
query_string: {
query: ""
}
};
searchQuery.query_string[key] = chip.value;
query.bool.filter.push(searchQuery);
});
console.log(JSON.stringify(query));

Simple method to modify json keys casing to camelCase from snake_case

I have lots of json parsing and all the json I am receiving is having keys in snake case like user_name. And for parsing I need it in camel case like userName.
The sample json file would look like this:
[{
"user_id" : 1,
"user_name" : "Abcd"
},
{
"org_id" : 11,
"org_name" : "some_name"
}
...
]
Expected output:
[{
"userId" : 1,
"userName" : "Abcd"
},
{
"orgId" : 11,
"orgName" : "some_name"
}
...
]
The json I am receiving is not having any particular fixed notation and it can be anything. But all the keys will be in snake case. And I need to convert it to camelCase.
What I cannot do is, find and replace, because it also replace snake casing strings in values as well.
Is there any easy method, which can do the same?
You can use npm package called: camelcase-keys-deep
https://www.npmjs.com/package/camelcase-keys-deep
You can do the following:
var keys = [];//this will contain the json with desired output
for(var i = 0;i<myObj.length;i++)//myObj is the variable that contains your json
{
Object.keys(myObj[i]).forEach(function(key){
if(keys.indexOf(key) == -1)
{
var newValue = {};
var value = myObj[i][key];
key = key.replace(/_([a-z])/g, function (g) { return g[1].toUpperCase(); });
newValue[key] = value;
keys.push(newValue);
}
});
}
//console.log(keys);
Hope this helps :)
So AKSHAY JAIN's implementation is pretty solid but it will delete the properties that are not in snake case. I fixed the implementation.
var arr = [{
"user_id": 1,
"user_name": "Abcd"
},
{
"org_id": 11,
"org_name": "some_name"
},
{
"personId": 12,
"personName": "otherName"
}];
arr.forEach(a => {
Object.keys(a).forEach(k => {
newK = k.replace(/(\_\w)/g, (m) => m[1].toUpperCase());
if (newK != k) {
a[newK] = a[k];
delete a[k];
}
});
});
console.log(arr);
If u use lodash I would suggest my solution for this:
snake_case -> camelCase
function camelCaseDeep(anything) {
const thing = _.cloneDeep(anything);
if (
_.isEmpty(thing) ||
(!_.isObject(thing) && !_.isArray(thing))
) {
return thing;
}
if (_.isArray(thing)) {
const arr = thing;
return arr.map(el => camelCaseDeep(el))
}
// thing can be only not empty object here
const objWithMappedKeys = _.mapKeys(thing, (value, key) => _.camelCase(key));
const objWithMappedValues = _.mapValues(objWithMappedKeys, value => camelCaseDeep(value));
return objWithMappedValues;
}
camelCase -> snake_case
function snakeCaseDeep(anything) {
const thing = _.cloneDeep(anything);
if (
_.isEmpty(thing) ||
(!_.isObject(thing) && !_.isArray(thing))
) {
return thing;
}
if (_.isArray(thing)) {
const arr = thing;
return arr.map(el => snakeCaseDeep(el))
}
// thing can be only not empty object here
const objWithMappedKeys = _.mapKeys(thing, (value, key) => _.snakeCase(key));
const objWithMappedValues = _.mapValues(objWithMappedKeys, value => snakeCaseDeep(value));
return objWithMappedValues;
}
var arr = [{
"user_id": 1,
"user_name": "Abcd"
},
{
"org_id": 11,
"org_name": "some_name"
}
];
arr.forEach(a => {
Object.keys(a).forEach(k => {
newK = k.replace(/(\_\w)/g, (m) => m[1].toUpperCase());
a[newK] = a[k];
delete a[k];
});
});
console.log(arr);

Categories