Javascript conditionally adding the nested object using map() operator - javascript

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

Related

Create new object with unique elements from objects of array of objects

I have an Array of Objects. Every object in this Array has some Keypairs. One of this Keypairs ("obj", for example) is an Array of Objects too.
Example what I have:
const arrOfObj = [
{
"id": 1
"obj": {
"arr1": ["arr1-1"],
"arr2": ["arr2-1", "arr2-2"],
"arr3": ["arr3-1", "arr3-2"]
}
},
{
"id": 1
"obj": {
"arr1": ["arr1-2"],
"arr2": ["arr2-1", "arr2-3"],
"arr3": ["arr3-1", "arr3-3"],
"arr4": ["arr4-1"],
}
},
];
I need to get new Object of "obj" Objects with unique keys and unique elements inside them.
Example what I need:
const newObj = {
"arr1": ["arr1-1", "arr1-2"],
"arr2": ["arr2-1", "arr2-2", "arr2-3"],
"arr3": ["arr3-1", "arr3-2", "arr3-3"],
"arr4": ["arr4-1"],
}
All of this comes dynamically from API by request, so I don`t know the names of this keypairs, but i need to store them.
I have Solution, but I`m new in JavaScript, and want to know how to simplify and improve my poor Code.
1. First, I`m defining the new Object and retrieving the Names for his keypairs from "arrOfObj".
let filterObj = {};
arrOfObj.forEach(function (item) {
for (let key in item.obj) {
filterObj[key] = [];
}
});
2. After that I`m getting all the Elements of every Array from "arrOfObj" and store them in new Object "filterObj" in the Keypair with the same Name.
arrOfObj.forEach(function (item) {
for (let key in item.obj) {
for (let element = 0; element < item.obj[key].length; element++) {
filterObj[key].push(item.obj[key][element]);
}
}
});
3. To the end I`m filtering Arrays to get unique Elements only.
for (let key in filterObj) {
filterObj[key] = Array.from(new Set(filterObj[key]));
}
It works, I`ve got what I want, but it seems to much monstrously. How this code can be simplified the best way?
Thanks for the help and advices.
You can use some destructuring and Object.entries() and Object.keys() to streamline this and do everything to the new Object only
const newObj = {}
arrOfObj.forEach(({obj}) => {
Object.entries(obj).forEach(([k, arr]) => {
newObj[k] = newObj[k] || [];
newObj[k].push(...arr);
})
});
Object.keys(newObj).forEach(k => newObj[k] = [...new Set(newObj[k])]);
console.log(newObj)
<script>
const arrOfObj=[{id:1,obj:{arr1:["arr1-1"],arr2:["arr2-1","arr2-2"],arr3:["arr3-1","arr3-2"]}},{id:1,obj:{arr1:["arr1-2"],arr2:["arr2-1","arr2-3"],arr3:["arr3-1","arr3-3"],arr4:["arr4-1"]}}];
</script>
Another solution using Object#fromEntries, Array#reduce, Object#entries, Array#forEach, Set, and Map:
const arrOfObj = [ { "id": 1, "obj": { "arr1": ["arr1-1"], "arr2": ["arr2-1", "arr2-2"], "arr3": ["arr3-1", "arr3-2"] } }, { "id": 1, "obj": { "arr1": ["arr1-2"], "arr2": ["arr2-1", "arr2-3"], "arr3": ["arr3-1", "arr3-3"], "arr4": ["arr4-1"] } } ];
const filterObj =
// transform the resulting list of key-values pairs to an object at the end
Object.fromEntries(
// get a map of array name as key and its unique items as value
[...arrOfObj.reduce((map, { obj = {} }) => {
// iterate over current element's object to update the map
Object.entries(obj).forEach(([currentKey, currentValues]) => {
const keyValues = [...(map.get(currentKey) || []), ...currentValues];
map.set(currentKey, [...new Set(keyValues)]);
});
return map;
}, new Map)]
);
console.log(filterObj);

How to iterate through out an array with a value path as reference

I don't really know how to express what I want, but I'll try.
So, I have an object with an array inside with the name of recipes, that I receive from my API, and a valuePath which is an object:
Object
{
recipes: [
{
test: {
items: [
{
type: 'test1',
}
]
}
}
]
}
ValuePath
{
"allRecipes": {
"array": "recipes",
"values": {
"allTypes": {
"array": "test",
"values": {
"type": "type"
}
}
}
}
}
Briefly what I have to do, is iterate over the array recipes through out the valuePath, dynamically, because the array and the values can change. I don't really know how to explain it better and how to iterate thought deeply nested objects/array's having a valuePath as a reference to find the values.
What I've tried so far...
export const test = (object, valuePath) => {
for (const prop in valuePath) {
object = object[valuePath[prop].array]; // find the array
if (Array.isArray(object)) {
object.forEach(objRef => {
console.log('valueRef', objRef);
});
}
console.log('props->', valuePath[prop].values); // find the values
}
};
I think i need a recursion, but have no clue how to do one.
If I understood your problem, this could be an implementation...
If you run it with your data and path, it will return test1.
// INPUTS
const data = {
recipes: [
{
test: {
items: [
{
type: 'test1',
}
]
}
}
]
}
const path = {
"allRecipes": {
"array": "recipes",
"values": {
"allTypes": {
"array": "test",
"values": {
"type": "type"
}
}
}
}
}
// this is just an helper method for arrays...
Array.prototype.first = function () { return this[0] }
// this is an helper function that tells us whether
// a path object is still traversable.
// from what I understood, if it contains an `array` property
// we should follow it...
const isTraversable = path => !!path.array
// this is the actual implementation of the algorithm
const traverse = (data, path) => {
const nextPath = Object.values(path).first()
if ( isTraversable(nextPath) ) {
const array = data[nextPath.array]
// I noticed that at a certain point in the data object,
// we need to traverse an array, and in another it is an
// object with an `items` property.
// this next lines helps determine how go down
const nextData = Array.isArray(array) ? array.first() : array.items
// we recurse on the traversed data and path
return traverse(nextData, nextPath.values)
}
return data.first()[path.type]
}
console.log(traverse(data, path))
Please try this, I hope it will help you..
let obj = {
recipes: [
{
test: {
items: [
{
type: 'test1',
},
],
},
},
],
};
obj.recipes.forEach(test => {
test.test.items.forEach(items => {
console.log(items.type);
});
});

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

Creating a new key from child elements in Json with Javascript

I want to create new key from child values inside the json with ES6/ES5. I tried with arrow functions but i couldn't get the result. Firstly you can see my part of json in below,
[
{
matchId:307,
matchStatusId:5,
matchHomeScore:0,
matchAwayScore:0,
matchTime:0,
homeClubId:608,
homeClub:{
clubId:608,
clubName:"Annaba"
},
awayClubId:609,
awayClub:{
clubId:609,
clubName:"Bazer Sakhra"
},
leagues:[
{
leagueId:65,
parentLeagueId:null,
leagueName:"ALGERIA"
},
{
leagueId:66,
parentLeagueId:65,
leagueName:"Algeria Cup"
}
]
},
]
I want to create new parent key. It will get the values from values of child items and it will combine. Child item numbers changeable. Not everytime 2 items.
leaguesGeneral:"ALGERIA - Algeria Cup"
leaguesGeneral:"ALGERIA - Algeria Cup"
leagues: [
{
leagueId:65,
parentLeagueId:null,
leagueName:"ALGERIA"
},
{
leagueId:66,
parentLeagueId:65,
leagueName:"Algeria Cup"
}
]
I found this method. But it combines everything from the parent.
data = data.map(function (x) {
var keys = Object.keys(x);
x.newKeyValue = keys.map(key => x[key]).join('-');
return x;
});
The leaguesGeneral key can be added to each object in the array using
o.leagues.map(({ leagueName }) => leagueName).join(' - ')
const data = [
{
matchId:307,
matchStatusId:5,
matchHomeScore:0,
matchAwayScore:0,
matchTime:0,
homeClubId:608,
homeClub:{
clubId:608,
clubName:"Annaba"
},
awayClubId:609,
awayClub:{
clubId:609,
clubName:"Bazer Sakhra"
},
leagues:[
{
leagueId:65,
parentLeagueId:null,
leagueName:"ALGERIA"
},
{
leagueId:66,
parentLeagueId:65,
leagueName:"Algeria Cup"
}
]
},
];
const result = data.map(o => {
if (o.leagues) {
o.leaguesGeneral = o.leagues.map(({ leagueName }) => leagueName).join(' - ');
}
return o;
})
console.log(result)

JSON file to map

I have a .json datastore and want to use Map() specific methods to get the values.
Here is some example of my .json file:
"program": [
{
"name": "program1",
"phases": [
{
"name": "A",
"cycles": [
{
"name": "day 1",
"exercises": [
{
"name": "workout1",
"set": 4,
"repetitions": 8
},
{
"name": "workout2",
"set": 4,
"repetitions": 8
}
]
}
]
}
]
}
]
My goal is to do something like this:
console.log(program.get("program1").phases.get("A").name);
I have a similar function which converts a .json file to a map
const datastore = require("./datastore.json");
function toMap(array, prop) {
const map = new Map();
for(const el of array) map.set(el[prop], el);
return map;
}
var program = toMap(datastore, "name");
but this converts only one "Map-layer", so I can only perform one .get(), no deeper ones.
Next thing is, that I want to modify the Map and save it back to the datastore.
You have to use recursion to fix your toMap() function. Please see fixed version below
function toMap(array, prop) {
const map = new Map();
for(const el of array) {
map.set(el[prop], el);
for(var key in el) {
if (Array.isArray(el[key])) {
el[key] = toMap(el[key], prop);
}
}
}
return map;
}
I fixed some bugs from #Vipin Kumar's version:
function toMap(array, key) {
const map = new Map();
for(const element of array) {
if (element[key]) {
map.set(element[key], element);
}
for(var subkey in element) {
if (Array.isArray(element[subkey])) {
if (element[subkey][0][key]) {
element[subkey] = toMap(element[subkey], key);
}
}
}
}
return map;
}
It will only convert arrays with given key to a map, instead of converting every array to a map.

Categories