DEMO
(Please check the browser console for output)
I have a JSON customerItemResponse in a format
{
"totalResults": someNumber,
"results": [
{
"totalItem": 406,
"customerId": "10000"
},
{
"totalItem": 468,
"customerId": "10001"
},
{
"totalItem": 20,
"customerId": "10002"
},
...
Then I have another JSON customerInfo:
{
"totalResults": someNumber,
"results": [
{
"customerId": "10000",
"region": "4",
"area": "42",
},
{
"customerId": "10001",
"region": "4",
"area": "43",
},
{
"customerId": "10002",
"region": "5",
"area": "52",
},
Now I have to create a JSON in a format
[
{
region:'4'
regionDetails:[
{
area:'42'
customerDetails:[
{
customerId:'10000'
totalItem:406
},
{
customerId:'10005'
totalItem:301
},
]
},
{
area:'11'
customerDetails:[
{
customerId:'10010'
totalItem:11
},
{
customerId:'10021'
totalItem:105
},
]
},
]
},
{
region:'5'
regionDetails:[
{
area:'52'
customerDetails:[
{
customerId:'10002'
totalItem:52
},
{
customerId:'10027'
totalItem:310
},
]
},
{
area:'41'
customerDetails:[
{
customerId:'10017'
totalItem:109
},
{
customerId:'10041'
totalItem:450
},
]
},
]
}
]
This is the logic I have written:
customerData=<CustomerDataInterface[]>[]
mapJson() {
this.customerItemResponse.map((res, index) => {
this.customerInfo.find((obj) => {
if (obj.customerId == res.customerId) {
this.customerData.length
? this.customerData.map((data, index1) => {
if (data.region == obj.region) {
data.regionDetails.length
? data.regionDetails.map((regDetails, index2) => {
if (regDetails.area == obj.area) {
regDetails.dealerDetails.push({
customerId: obj.customerId,
totalItem: res.totalItem,
});
return;
}
if (index2 == data.regionDetails.length - 1) {
data.regionDetails.push({ area: obj.area, dealerDetails: [] });
}
})
: data.regionDetails.push({ area: obj.area, dealerDetails: [] });
return;
}
if (index1 == this.customerData.length - 1) {
this.customerData.push({ region: obj.region, regionDetails: [] });
}
})
: this.customerData.push({ region: obj.region, regionDetails: [] });
}
});
});
console.log(this.customerData);
}
Now the output of the console has several region repeated. And suppose if I have 6 unique region but the this.customerData.length is 31.
I think return; is not working as expected. And is not skipping the subsequent element.
here is an efficient way to resolving the issue using js Maps. We can build maps with info about corresponding region and then areas. and after the data is built into maps - convert it back to simple js structures, such as object and arrays
mapJson() {
const customerToTotalMap = new Map(this.customerItemResponse.map(({customerId, totalItem}) => [customerId, totalItem]));
const regionsMap = new Map();
for(let {customerId, region, area} of this.customerInfo) {
let regionAreas;
if(regionsMap.has(region)) {
regionAreas = regionsMap.get(region);
} else {
regionAreas = new Map();
regionsMap.set(region, regionAreas);
}
let areaInfo;
if(regionAreas.has(area)) {
areaInfo = regionAreas.get(area);
} else {
areaInfo = [];
regionAreas.set(area, areaInfo);
}
areaInfo.push({customerId, totalItem: customerToTotalMap.get(customerId)});
}
this.customerData = [...regionsMap.entries()].map(([region, areas]) => ({
region,
regionDetails: [...areas.entries()].map(([area, customerDetails]) => ({
area,
customerDetails
}))
}))
console.log(this.customerData);
}
This is similar to #Andrei's answer. It creates an object literal as mapper. Also, it uses mapping between the region and area when they are created. So, finally you can just get the values of the regionMapper object without going through the mapper objects again
const customerItemResponse=[{customerId:10000,totalItem:77},{customerId:10001,totalItem:37},{customerId:10002,totalItem:295},{customerId:10003,totalItem:458},{customerId:10004,totalItem:248},{customerId:10005,totalItem:35},{customerId:10006,totalItem:280},{customerId:10007,totalItem:147},{customerId:10008,totalItem:439},{customerId:10009,totalItem:401},{customerId:10010,totalItem:489},{customerId:10011,totalItem:414},{customerId:10012,totalItem:287},{customerId:10013,totalItem:391},{customerId:10014,totalItem:125},{customerId:10015,totalItem:207},{customerId:10016,totalItem:197},{customerId:10017,totalItem:151},{customerId:10018,totalItem:225},{customerId:10019,totalItem:333},{customerId:10020,totalItem:361},{customerId:10021,totalItem:225},{customerId:10022,totalItem:242},{customerId:10023,totalItem:150},{customerId:10024,totalItem:52},{customerId:10025,totalItem:475},{customerId:10026,totalItem:494},{customerId:10027,totalItem:30},{customerId:10028,totalItem:189},{customerId:10029,totalItem:112},{customerId:10030,totalItem:482},{customerId:10031,totalItem:283},{customerId:10032,totalItem:159},{customerId:10033,totalItem:440},{customerId:10034,totalItem:461},{customerId:10035,totalItem:76},{customerId:10036,totalItem:84},{customerId:10037,totalItem:392},{customerId:10038,totalItem:296},{customerId:10039,totalItem:293},{customerId:10040,totalItem:135},{customerId:10041,totalItem:348},{customerId:10042,totalItem:338},{customerId:10043,totalItem:444},{customerId:10044,totalItem:15},{customerId:10045,totalItem:32},{customerId:10046,totalItem:67},{customerId:10047,totalItem:277},{customerId:10048,totalItem:65},{customerId:10049,totalItem:95},{customerId:10050,totalItem:290}],
customerInfo=[{customerId:10000,region:"3",area:"32"},{customerId:10001,region:"2",area:"22"},{customerId:10002,region:"2",area:"25"},{customerId:10003,region:"3",area:"31"},{customerId:10004,region:"2",area:"25"},{customerId:10005,region:"1",area:"11"},{customerId:10006,region:"1",area:"14"},{customerId:10007,region:"5",area:"55"},{customerId:10008,region:"5",area:"51"},{customerId:10009,region:"4",area:"45"},{customerId:10010,region:"1",area:"14"},{customerId:10011,region:"1",area:"12"},{customerId:10012,region:"3",area:"33"},{customerId:10013,region:"2",area:"25"},{customerId:10014,region:"4",area:"41"},{customerId:10015,region:"3",area:"32"},{customerId:10016,region:"5",area:"55"},{customerId:10017,region:"2",area:"23"},{customerId:10018,region:"3",area:"33"},{customerId:10019,region:"5",area:"51"},{customerId:10020,region:"4",area:"42"},{customerId:10021,region:"1",area:"12"},{customerId:10022,region:"1",area:"14"},{customerId:10023,region:"1",area:"14"},{customerId:10024,region:"1",area:"13"},{customerId:10025,region:"4",area:"45"},{customerId:10026,region:"3",area:"34"},{customerId:10027,region:"2",area:"24"},{customerId:10028,region:"4",area:"45"},{customerId:10029,region:"2",area:"22"},{customerId:10030,region:"2",area:"22"},{customerId:10031,region:"2",area:"21"},{customerId:10032,region:"3",area:"33"},{customerId:10033,region:"1",area:"11"},{customerId:10034,region:"3",area:"33"},{customerId:10035,region:"3",area:"32"},{customerId:10036,region:"2",area:"22"},{customerId:10037,region:"4",area:"41"},{customerId:10038,region:"3",area:"31"},{customerId:10039,region:"5",area:"51"},{customerId:10040,region:"2",area:"23"},{customerId:10041,region:"4",area:"45"},{customerId:10042,region:"1",area:"14"},{customerId:10043,region:"5",area:"54"},{customerId:10044,region:"3",area:"34"},{customerId:10045,region:"5",area:"51"},{customerId:10046,region:"4",area:"42"},{customerId:10047,region:"5",area:"53"},{customerId:10048,region:"1",area:"11"},{customerId:10049,region:"3",area:"35"},{customerId:10050,region:"5",area:"51"}];
const customerItemMapper = {}
for (const c of customerItemResponse)
customerItemMapper[c.customerId] = c.totalItem
const regionMapper = {},
areaMapper = {};
for (const { customerId, region, area } of customerInfo) {
let regionKey = `Region_${region}`,
areaKey = `Area_${area}`,
totalItem = customerItemMapper[customerId];
if (!(regionKey in regionMapper))
regionMapper[regionKey] = { region, regionDetails: [] }
if (!(areaKey in areaMapper)) {
const o = { area, customerDetails: [] }
areaMapper[areaKey] = o;
regionMapper[regionKey].regionDetails.push(o) // area-region relation
}
areaMapper[areaKey].customerDetails.push({ customerId, totalItem })
}
console.log(Object.values(regionMapper))
I have JSON object that looks like the snippet below.
I can navigate to the fields that I am interested to query, which match the pattern useful*
contents[0].mainArea[1].rec[0].attributes['useful.k1']
contents[0].mainArea[1].rec[1].attributes['useful.k1']
Is it possible to write a Lodash command that can extract/return the indices in bold above? I need the indices as I will later use these indices to create an assertion.
I am not sure where to start looking. Any ideas as to which commands I need to consider will be greatly appreciated.
{
"contents": [
{
"leftArea": [],
"mainArea": [
{
"key11": "val11",
"key12": [
{
"subkey11": "subValue11",
"subkey12": "subValue12"
},
{
"subkey21": "subValue21",
"subkey22": "subValue22"
}
]
},
{
"key21": "val21",
"rec": [
{
"attributes": {
"useful.k1": [
"value"
],
"useful.k2": [
"value"
],
"useful.k3": [
"value"
]
}
},
{
"attributes": {
"useful.k1": [
"value"
],
"useful.k2": [
"value"
],
"useful.k3": [
"value"
]
}
},
{
"notuseful": "value",
"notuseful2": [
{
"key1": "value",
"key2": "value"
},
{
"key1": "value",
"key2": "value"
},
{
"key1": "value",
"key2": "value"
}
]
}
]
}
]
}
]
}
I don't know if there is any specific lodash command for your problem, but you can use some functions to find your desired indices by some code like below:
const keys = require('lodash/keys');
function findUsefulKeys(data) {
const indices = [];
for (let i = 0; i < data.contents.length; i++) {
const mainAreaIndiesWithUsefulKeys = findUsefulKeysInContent(data.contents[i]);
if (mainAreaIndiesWithUsefulKeys.length > 0) {
indices.push({
contentIndex: i,
mainAreaIndices: mainAreaIndiesWithUsefulKeys
});
}
}
return indices;
}
function findUsefulKeysInContent(content) {
const mainAreaIndices = [];
if (!content.mainArea || !content.mainArea.length || content.mainArea.length === 0) {
return mainAreaIndices;
}
for (let i = 0; i < content.mainArea.length; i++) {
const recIndices = findRecsWithUsefulKeysInMainArea(content.mainArea[i]);
if (recIndices.length > 0) {
mainAreaIndices.push({
mainAreaIndex: i,
recIndices: recIndices
});
}
}
return mainAreaIndices;
}
function findRecsWithUsefulKeysInMainArea(mainArea) {
const recIndices = [];
if (!mainArea.rec || !mainArea.rec.length || mainArea.rec.length === 0) {
return recIndices;
}
for (let i = 0; i < mainArea.rec.length; i++) {
if (recHasUsefulKeys(mainArea.rec[i])) {
recIndices.push(i);
}
}
return recIndices;
}
function recHasUsefulKeys(rec) {
if (!rec || !rec.attributes) {
return false;
}
const attributeKeys = keys(rec.attributes);
for (let i = 0; i < attributeKeys.length; i++) {
if (attributeKeys[i].startsWith('useful')) {
return true;
}
}
return false;
}
// data is you json data
const data = require('./data');
console.log(JSON.stringify(findUsefulKeys(data)));
The above code with your example json data will print
[{"contentIndex":0,"mainAreaIndices":[{"mainAreaIndex":1,"recIndices":[0,1]}]}]
You can loop to get the value from JSON like below:
const data = {
"contents": [
{
"leftArea": [],
"mainArea": [
{
"key11": "val11",
"key12": [
{
"subkey11": "subValue11",
"subkey12": "subValue12"
},
{
"subkey21": "subValue21",
"subkey22": "subValue22"
}
]
},
{
"key21": "val21",
"rec": [
{
"attributes": {
"useful.k1": [
"value"
],
"useful.k2": [
"value"
],
"useful.k3": [
"value"
]
}
},
{
"attributes": {
"useful.k1": [
"value"
],
"useful.k2": [
"value"
],
"useful.k3": [
"value"
]
}
},
{
"notuseful": "value",
"notuseful2": [
{
"key1": "value",
"key2": "value"
},
{
"key1": "value",
"key2": "value"
},
{
"key1": "value",
"key2": "value"
}
]
}
]
}
]
}
]
}
const useful = data.contents[0].mainArea[1].rec;
for(const v in useful)
{
//console.log(useful[v].attributes); // Oject
for( let vv in useful[v].attributes) // for for every single value
{
console.log(useful[v].attributes[vv]);
}
}
I am trying to create a function that will loop over a Hierarchy of arrays and concat them together. I am struggling to think of a way to do this.
I call a Web API that returns me some data which has the same properties and layout but can differ in how many Hierarchy layer array there is.
Folders: A folder can contain 'Routes' but can also contain another folder inside of it which can then also contain more 'Routes' etc
Routes: A single object which is a route.
For example:
{
"id":1,
"folders":[
{
"id":2,
"folders":[
{
"id":3,
"folders":[],
"routes":[]
}
],
"routes":[
{
"id":1002,
"name":"Route3"
},
{
"id":1003,
"name":"Route4"
}
]
}
],
"routes":[
{
"id":1000,
"name":"Route1"
},
{
"id":1001,
"name":"Route2"
}
]
}
I need to be able to keep going deeper into the hierarchy and concat all of the Routes arrays with the Folders array, so I can bind the Kendo TreeView with a single child which will be the Folders array.
So far I have tried:
for (var i = 0; i < Folders.length; i++) {
if (Folder[i].Folders.length > 0) {
for (var e = 0; e < Folder[i].Folder[e].length; e++) {
if (Folder[i].Folders[e].length > 0) {
...
}
}
}
}
The problem with that method is that I will never know how many layer there will be and therefore is not a viable method.
The result I need is the above example to look like:
{
"id":1,
"folders":[
{
"id":2,
"folders":[
{
"id":3,
"folders":[
],
"routes":[
]
},
{
"id":1002,
"name":"Route3"
},
{
"id":1003,
"name":"Route4"
}
]
},
{
"id":1000,
"name":"Route1"
},
{
"id":1001,
"name":"Route2"
}
]
}
var flatRoutes = function(folders){
for(var i in folders){
var cur = folders[i];
if(cur.hasOwnProperty('folders')){
flatRoutes(cur.folders);
}
if(cur.hasOwnProperty('routes')){
for(var i in cur.routes){
cur.folders.push(cur.routes[i]);
}
delete cur.routes;
}
}
return folders;
}
var json = {
"id":1,
"folders":[
{
"id":2,
"folders":[
{
"id":3,
"folders":[],
"routes":[]
}
],
"routes":[
{
"id":1002,
"name":"Route3"
},
{
"id":1003,
"name":"Route4"
}
]
}
],
"routes":[
{
"id":1000,
"name":"Route1"
},
{
"id":1001,
"name":"Route2"
}
]
}
var routes = flatRoutes([json]);
console.log(routes);
To remove a property from ab object, you can make use of delete object.property.
I have taken the following step, I think it gives the correct result.
Create a modular function - formatData
Check if folders array has any data or not
If folders array have data, call formatData for every folder in it
Then, check for the routes array. If no routes, return the data
If routes array have data, just push each route into the folder array and remove the routes property from the object.
let data = {
"id": 1,
"folders": [{
"id": 2,
"folders": [{
"id": 3,
"folders": [],
"routes": []
}],
"routes": [{
"id": 1002,
"name": "Route3"
},
{
"id": 1003,
"name": "Route4"
}
]
}],
"routes": [{
"id": 1000,
"name": "Route1"
},
{
"id": 1001,
"name": "Route2"
}
]
};
function formatData(data) {
if (data.folders.length) {
data.folders.forEach(folder => {
return formatData(folder);
});
}
if (data.routes.length) {
data.routes.forEach(route => {
data.folders.push(route);
});
delete data.routes;
}
return data;
}
console.log(formatData(data));
You could use an iterative and recursive approach.
var data = { id: 1, folders: [{ id: 2, folders: [{ id: 3, folders: [], routes: [] }], routes: [{ id: 1002, name: "Route3" }, { id: 1003, name: "Route4" }] }], routes: [{ id: 1000, name: "Route1" }, { id: 1001, name: "Route2" }] },
result = [data].map(function iter(o) {
return {
id: o.id,
folders: (o.folders && o.folders.map(iter) || []).concat(o.routes || [])
};
})[0];
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I am trying to return all objects that have a specific 'id' in the nested array. In the sample data, I'd like to return all person objects with hobbies id of 2 (hiking).
The other question addresses the problem of finding all values in an array based on an object value.
This question differs from the previous because I need to return all objects based on a value inside of a nested array.
[
{
"id":111222,
"name":"Faye",
"age":27,
"hobbies":[
{
"id":2,
"name":"hiking"
},
{
"id":3,
"name":"eating"
}
]
},
{
"id":223456789001,
"name":"Bobby",
"age":35,
"hobbies":[
{
"id":2,
"name":"hiking"
},
{
"id":4,
"name":"online gaming"
}
]
}
]
function hasHobby(person, hobbyId) {
return person.hobbies.some(function(hobby) {
return hobby.id === hobbyId;
});
}
function filterByHobby(people, hobbyId) {
return people.filter(function(person) {
return hasHobby(person, hobbyId);
});
}
If you wanna use the new cool ES6 syntax:
function filterByHobby(people, hobbyId) {
return people.filter(
person => person.hobbies.some(
hobby => hobby.id === hobbyId
)
);
}
var arr = [
{
"id":111222,
"name":"Faye",
"age":27,
"hobbies":[
{
"id":2,
"name":"hiking"
},
{
"id":3,
"name":"eating"
}
]
},
{
"id":223456789001,
"name":"Bobby",
"age":35,
"hobbies":[
{
"id":2,
"name":"hiking"
},
{
"id":4,
"name":"online gaming"
}
]
}
];
arr.filter(function(obj) {
var hobbies = obj.hobbies;
var x = hobbies.filter(function(hob) {
if (hob.id == "2") return true;
});
if (x.length > 0) return true;
});
Try this, I think its solve your proble:
var arr = [{
"id": 111222,
"name": "Faye",
"age": 27,
"hobbies": [{
"id": 2,
"name": "hiking"
}, {
"id": 3,
"name": "eating"
}]
}, {
"id": 223456789001,
"name": "Bobby",
"age": 35,
"hobbies": [{
"id": 2,
"name": "hiking"
}, {
"id": 4,
"name": "online gaming"
}]
}];
var x = arr.filter(function(el) {
var rnel = el.hobbies.filter(function(nel) {
return nel.id == 2;
});
return rnel.length > 0 ? true :false;
});
alert(x.length);