Objects nested in objects iteration? - javascript

I want to write a function that will find the Categoy[3].options[3].label and matches it to the letter "D".
How do I iterate through the nested objects below to go through the TagCategory.options and search for the option that matches to the letter "D"? If it matches the letter "D", it should return true.
"Category":
[
{
"field": "A",
"options": [
{"tag": "100", "value": "yes"}
],
"label": "Red"
},
{
"field": "tanks",
"type": true,
"options": [
{"tag_value": "4", "value": "4", "label": "A"},
{"tag_value": "3", "value": "3", "label": "B"},
{"tag_value": "2", "value": "2", "label": "C"},
{"tag_value": "1", "value": "1", "label": "D"},
{"tag_value": "5", "value": "5", "label": "E"}
],
"label": "Tanks"
}
]

Something like this?
function findLabel(arr, label) {
for (var i in arr) {
const options = arr[i].options;
const find = options.find(o => o.label == label);
if (find) {
return true;
}
}
return false;
}
const test = findLabel(Category, "D");
Category is your array

function check(category, letter){
var obj;
var optionObj;
for(var i = 0; i < category.length; i++){
obj = Category[i];
options = obj.options;
for(var j = 0; j < options.length; j++){
optionObj = option[j];
if(optionObj.label === letter) return true;
}
}
return false;
}

Here is a rough example of what I mean by a nested for loop.
category = {...}; // your object above
for (i in category) {
field = category[i];
for (option_i in field.options) {
// This is the nested for loop - called thus because it's inside one.
if (field.options[option_i].label == "D"){
return true;
}
}
}

You can use Array.prototype.some()
let optionHasLabelDBool = obj.Category.some(({options}) =>
options.some(({label}) => label === "D"));
let obj = {
"Category":
[{
"field": "A",
"options": [{
"tag": "100",
"value": "yes"
}],
"label": "Red"
},
{
"field": "tanks",
"type": true,
"options": [{
"tag_value": "4",
"value": "4",
"label": "A"
}, {
"tag_value": "3",
"value": "3",
"label": "B"
}, {
"tag_value": "2",
"value": "2",
"label": "C"
}, {
"tag_value": "1",
"value": "1",
"label": "D"
}, {
"tag_value": "5",
"value": "5",
"label": "E"
}],
"label": "Tanks"
}
]
};
let optionHasLabelD = obj.Category.some(({options}) => options.some(({label}) => label === "D"));
console.log(optionHasLabelD);
Using a function, without arrow function or object destructing
function checkOption(array, prop, key, value) {
return array.some(function(obj) {
return obj[prop].some(function(match) {
return match[key] === value;
});
});
}
let optionHasLabelDBool = checkOption(obj.Category, "options", "label", "D");
let obj = {
"Category": [{
"field": "A",
"options": [{
"tag": "100",
"value": "yes"
}],
"label": "Red"
},
{
"field": "tanks",
"type": true,
"options": [{
"tag_value": "4",
"value": "4",
"label": "A"
}, {
"tag_value": "3",
"value": "3",
"label": "B"
}, {
"tag_value": "2",
"value": "2",
"label": "C"
}, {
"tag_value": "1",
"value": "1",
"label": "D"
}, {
"tag_value": "5",
"value": "5",
"label": "E"
}],
"label": "Tanks"
}
]
};
function checkOption(array, prop, key, value) {
return array.some(function(obj) {
return obj[prop].some(function(match) {
console.log(match[key], value, match[key] === value);
return match[key] === value;
});
});
}
let optionHasLabelDBool = checkOption(obj.Category, "options", "label", "D");
console.log(optionHasLabelDBool);

Related

How to validate a list of objects value

My array look like this:
var theset=[
{
"set": "1",
"data": [
{ "field": "A", "value": "111", "check": true },
{ "field": "B", "value": "111", "check": false },
{ "field": "C", "value": "111", "check": true }
]
},
{
"set": "2",
"data": [
{ "field": "A", "value": "111", "check": true },
{ "field": "B", "value": "222", "check": false },
{ "field": "C", "value": "222", "check": true }
]
},
{
"set": "3",
"data": [
{ "field": "A", "value": "333", "check": true },
{ "field": "B", "value": "333", "check": false },
{ "field": "C", "value": "222", "check": true }
]
},
{
"set": "4",
"data": [
{ "field": "A", "value": "444", "check": true },
{ "field": "B", "value": "333", "check": false },
{ "field": "C", "value": "444", "check": true }
]
}
];
What I want to do is validate the "value" with other set on the same field only when the "check" is true.
The result is to return a true if there is a duplication of "value" in the set. The example will return a true because
set 1: having duplicate value for field A with set 2
set 2: having duplicate value for field A with set 1, duplicate value for field C with set 3
set 3: having duplicate value for field C with set 2
set 4: not consider as duplicate, even though the value of field B match with set 3 because of the check is false
so far I tried to do for loop on the list but this will have a lot of nested loop which is not efficient.
for(var i=0; i<theset.length; i++){
var checking = theset[i].data;
for(var j=0; j<checking.length; j++){
if(checking[j].check){
for(var k=0; k<theset.length; k++){
if(k!=i){
var checking2 = theset[k].data;
for(var l=0; l<checking2.length; l++){
...
}
}
}
}
}
}
Can anybody help me?
one way can be for each set to filter the duplicate data with array.filter
then if there is duplicate do the wanted treatment
var theset = [
{
"set": "1",
"data": [
{ "field": "A", "value": "111", "check": true },
{ "field": "B", "value": "111", "check": false },
{ "field": "C", "value": "111", "check": true }
]
},
{
"set": "2",
"data": [
{ "field": "A", "value": "111", "check": true },
{ "field": "B", "value": "222", "check": false },
{ "field": "C", "value": "222", "check": true }
]
},
{
"set": "3",
"data": [
{ "field": "A", "value": "333", "check": true },
{ "field": "B", "value": "333", "check": false },
{ "field": "C", "value": "222", "check": true }
]
},
{
"set": "4",
"data": [
{ "field": "A", "value": "444", "check": true },
{ "field": "B", "value": "333", "check": false },
{ "field": "C", "value": "444", "check": true }
]
}
];
theset.forEach(set => {
var duplicate = set.data.filter(data => {
return theset.some(oneSet => oneSet.data.some(oneData => oneData.value === data.value));
});
if (duplicate.length) {
console.log(`the following set have duplicate ${set.set}`);
console.log(duplicate);
//treat as you want the set and the duplicate
}
});
i would do it with a hash map
const theset = [
{
"set": "1",
"data": [
{ "field": "A", "value": "111", "check": true },
{ "field": "B", "value": "111", "check": false },
{ "field": "C", "value": "111", "check": true }
]
},
{
"set": "2",
"data": [
{ "field": "A", "value": "111", "check": true },
{ "field": "B", "value": "222", "check": false },
{ "field": "C", "value": "222", "check": true }
]
},
{
"set": "3",
"data": [
{ "field": "A", "value": "333", "check": true },
{ "field": "B", "value": "333", "check": false },
{ "field": "C", "value": "222", "check": true }
]
},
{
"set": "4",
"data": [
{ "field": "A", "value": "444", "check": true },
{ "field": "B", "value": "333", "check": false },
{ "field": "C", "value": "444", "check": true }
]
}
];
const hashmap = {};
theset.forEach((item) => {
item.data.forEach((obj) => {
if (!obj.check) {
return;
}
const key = JSON.stringify(obj);
if (!hashmap[key]) {
hashmap[key] = [];
}
hashmap[key].push(item.set);
})
})
console.log('hashmap', hashmap)
as a result you would get the following output:
{
"obj1": ["1","2"],
"obj2": ["1"],
"obj3": ["2","3"],
"obj4": ["3"],
"obj5": ["4"],
"obj6": ["4"]
}
obj1 is common for set 1 and set 2
obj3 is common for set 2 and set 3
you have the all needed information for your conclusions
of course, you could use a custom hash function rather than JSON.stringify()

Preparing JSON tree structure from a JSON javascript

am looking for a JSON tree from a plain JSON.
below is the Input and expected output json.
the input json doesnt have any order
There can be multiple items at each level and each item can have n number of childrens.
leafFlag = 0 means no children
leafFlag = 1 means has chidlren
path is the property which tells where the item will be placed
The expectedOutputJson needs to be placed in a table to display level wise data
var inputJson = [
{
"level": "1",
"leafFlag": "1",
"path":"p123",
"name":"food"
},
{
"level": "1",
"leafFlag": "1",
"path":"r125",
"name":"car"
},
{
"level": "2",
"leafFlag": "0",
"path":"p123/p345",
"name":"apple"
},
{
"level": "2",
"leafFlag": "1",
"path":"p123/p095",
"name":"banana"
},
{
"level": "3",
"leafFlag": "0",
"path":"p123/p095/p546",
"name":"grapes"
},
{
"level": "2",
"leafFlag": "1",
"path":"r125/yhes",
"name":"tata"
},
]
var expectedOutput = [
{
"level": "1",
"leafFlag": "1",
"path": "p123",
"name": "food",
"children": [
{
"level": "2",
"leafFlag": "0",
"path": "p123/p345",
"name": "apple"
},
{
"level": "2",
"leafFlag": "1",
"path": "p123/p095",
"name": "banana",
"children": [
{
"level": "3",
"leafFlag": "0",
"path": "p123/p095/p546",
"name": "grapes"
}
]
}
]
},
{
"level": "1",
"leafFlag": "1",
"path": "r125",
"name": "car",
"children": [
{
"level": "2",
"leafFlag": "1",
"path": "r125/yhes",
"name": "tata",
"children": [
{
"level": "3",
"leafFlag": "0",
"path": "r125/yhes/sdie",
"name": "Range Rover"
}
]
},
{
"level": "2",
"leafFlag": "0",
"path": "r125/theys",
"name": "suzuki"
}
]
}
]
i tried the below code but not able to proceed further
private prepareTreeStructure = (inputJson) => {
const treeFormat = (key, index, result) => {
if (key.indexOf('/') === -1) {
result = [...result, ...d[key]];
} else {
result.forEach((item, index) => {
let splitKeyArray = key.split('/');
splitKeyArray.forEach((splitItem, splitIndex) => {
if (splitKeyArray.indexOf(item.path) !== -1) {
if (!item['children']) {
item['children'] = [];
}
if (splitKeyArray.length === splitIndex + 1) {
result[index]['children'] = [...result[index]['children'], ...d[key]];
}
}
});
if (splitKeyArray.indexOf(item.path) !== -1) {
if (!result[index]['children']) {
result[index]['children'] = [];
}
result[index]['children'] = [...result[index]['children'], ...d[key]];
}
});
}
return result;
}
const d = inputJson.reduce((acc, ele) => {
if (!acc[ele.path]) {
acc[ele.path] = [];
}
acc[ele.path].push(ele);
return acc;
}, {});
console.log('dddd ', d);
this.result = [];
Object.keys(d).forEach((key, index) => {
this.result = treeFormat(key, index, this.result);
console.log('out ', this.result);
})
}
prepareTreeStructure(inputJson);
I think you're looking for something like this, works for an n number of levels:
var inputJson = [
{
"level": "1",
"leafFlag": "1",
"path":"p123",
"name":"food"
},
{
"level": "1",
"leafFlag": "1",
"path":"r125",
"name":"car"
},
{
"level": "2",
"leafFlag": "0",
"path":"p123/p345",
"name":"apple"
},
{
"level": "2",
"leafFlag": "1",
"path":"p123/p095",
"name":"banana"
},
{
"level": "3",
"leafFlag": "0",
"path":"p123/p095/p546",
"name":"grapes"
},
{
"level": "2",
"leafFlag": "1",
"path":"r125/yhes",
"name":"tata"
}
]
And the actual code:
var output = [];
inputJson = inputJson.sort((a, b) => (parseInt(a.level) > parseInt(b.level)) ? 1 : -1)
inputJson.forEach(v => {
if (v.level == "1") {
v.children = [];
output.push(v);
}
else {
pathValues = v.path.split("/");
pathValues.pop();
var node = null;
var fullPath = "";
pathValues.forEach(p => {
fullPath = fullPath === "" ? p : fullPath + "/" + p;
node = (node == null ? output : node.children).find(o => o.path === fullPath);
})
node.children = node.children || [];
node.children.push(v);
}
})
console.log(output)
See the jsfiddle: https://jsfiddle.net/L984bo6x/8/
var inputJson = [
{
"level": "1",
"leafFlag": "1",
"path":"p123",
"name":"food"
},
{
"level": "1",
"leafFlag": "1",
"path":"r125",
"name":"car"
},
{
"level": "2",
"leafFlag": "0",
"path":"p123/p345",
"name":"apple"
},
{
"level": "2",
"leafFlag": "1",
"path":"p123/p095",
"name":"banana"
},
{
"level": "3",
"leafFlag": "0",
"path":"p123/p095/p546",
"name":"grapes"
},
{
"level": "2",
"leafFlag": "0",
"path":"r125/yhes",
"name":"tata"
},
]
var expectedOutput = [
{
"level": "1",
"leafFlag": "1",
"path": "p123",
"name": "food",
"children": [
{
"level": "2",
"leafFlag": "0",
"path": "p123/p345",
"name": "apple"
},
{
"level": "2",
"leafFlag": "1",
"path": "p123/p095",
"name": "banana",
"children": [
{
"level": "3",
"leafFlag": "0",
"path": "p123/p095/p546",
"name": "grapes"
}
]
}
]
},
{
"level": "1",
"leafFlag": "1",
"path": "r125",
"name": "car",
"children": [
{
"level": "2",
"leafFlag": "1",
"path": "r125/yhes",
"name": "tata",
"children": [
{
"level": "3",
"leafFlag": "0",
"path": "r125/yhes/sdie",
"name": "Range Rover"
}
]
},
{
"level": "2",
"leafFlag": "0",
"path": "r125/theys",
"name": "suzuki"
}
]
}
]
const groupByLevels = inputJson => {
var levelsObj = {};
inputJson.forEach(ele => {
if (ele.level === "1") {
if (!levelsObj["1"]) {
levelsObj["1"] = [];
}
levelsObj["1"].push(ele)
} else {
if (!levelsObj[ele.level]) {
levelsObj[ele.level] = {};
}
var parKey = ele.path.substr(0, ele.path.lastIndexOf('/'));
if (!levelsObj[ele.level][parKey]) {
levelsObj[ele.level][parKey] = [];
}
levelsObj[ele.level][parKey].push(ele);
}
})
return levelsObj;
}
const mergeByGroups = (currLevelArr, groupJSON) => {
currLevelArr.forEach(ele => {
if (ele.leafFlag == "0") {
return ele;
}
let nextLevel = parseInt(ele.level) + 1 + ""
let nextLevelArr = groupJSON[nextLevel][ele.path];
mergeByGroups(nextLevelArr, groupJSON)
ele.children = nextLevelArr;
})
}
const constructOutput = groupJSON => {
mergeByGroups(groupJSON["1"], groupJSON);
return groupJSON["1"];
}
console.log(JSON.stringify(constructOutput(groupByLevels(inputJson)), null, 4));

Group objects by keys in JavaScript [duplicate]

This question already has answers here:
Group By and Sum using Underscore/Lodash
(6 answers)
Closed 6 years ago.
I have an array of objects. These objects have a property called "type".
[{
"id": "1",
"type": "Plane",
"date": "date",
"value": "10",
"another_value": "10"
},
{
"id": "1",
"type": "Plane",
"date": "date",
"value": "15",
"another_value": "10"
},
{
"id": "1",
"type": "Car",
"date": "date",
"value": "25",
"another_value": "10"
},
{
"id": "1",
"type": "Car",
"date": "date",
"value": "25",
"another_value": "10"
}]
Now, I have a table where I want to display this. I want to display it in two rows, one row for "Plane" and one row for "Car" and in that row I want like the total sum of (value, and another_value)
So I'm thinking this is easiest accomplished by somehow going through my original array of objects and grouping them by "type". So I'll get an array of all objects with the type "Car", and one with "Plane".
I have lodash available, then I also need to find a way to sum the values, so I can display it in my table, in a column on each row.
Any advice on how I can accomplish this?
You can do this with plain JavaScript, using methods like map() and filter():
const arr = [{
"id": "1",
"type": "Plane",
"date": "date",
"value": "10",
"another_value": "10"
},
{
"id": "1",
"type": "Plane",
"date": "date",
"value": "15",
"another_value": "10"
},
{
"id": "1",
"type": "Car",
"date": "date",
"value": "25",
"another_value": "10"
},
{
"id": "1",
"type": "Car",
"date": "date",
"value": "25",
"another_value": "10"
}]
const sum = (a, b) => Number(a) + Number(b)
const cars = arr.filter(x => x.type === 'Car')
const planes = arr.filter(x => x.type === 'Plane')
const carsValue = cars.map(x => x.value).reduce(sum)
const planesValue = planes.map(x => x.value).reduce(sum)
const carsAnotherValue = cars.map(x => x.another_value).reduce(sum)
const planesAnotherValue = planes.map(x => x.another_value).reduce(sum)
You could first group all the objects by type, and then reduce all the value and another_value:
var types = _.groupBy(array, 'type');
var result = _.mapValues(types, function(value, key) {
return _.reduce(value, function(memo, v) {
return memo + +v.value + +v.another_value;
}, 0);
});
var array = [{
"id": "1",
"type": "Plane",
"date": "date",
"value": "10",
"another_value": "10"
},
{
"id": "1",
"type": "Plane",
"date": "date",
"value": "15",
"another_value": "10"
},
{
"id": "1",
"type": "Car",
"date": "date",
"value": "25",
"another_value": "10"
},
{
"id": "1",
"type": "Car",
"date": "date",
"value": "25",
"another_value": "10"
}];
var types = _.groupBy(array, 'type');
var result = _.mapValues(types, function(value, key) {
return _.reduce(value, function(memo, v) {
return memo + +v.value + +v.another_value;
}, 0);
});
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
Another approach
var count = _.countBy(data, 'type');
var sum = _.reduce(data, function(total, obj) {
total[obj.type] += Number(obj.value);
return total;
}, {
Car: 0,
Plane: 0
});
var data = [{
"id": "1",
"type": "Plane",
"date": "date",
"value": "10",
"another_value": "10"
},
{
"id": "1",
"type": "Plane",
"date": "date",
"value": "15",
"another_value": "10"
},
{
"id": "1",
"type": "Car",
"date": "date",
"value": "25",
"another_value": "10"
},
{
"id": "1",
"type": "Car",
"date": "date",
"value": "25",
"another_value": "10"
}];
var count = _.countBy(data, 'type');
var sum = _.reduce(data, function(total, obj) {
total[obj.type] += Number(obj.value);
return total;
}, {
Car: 0,
Plane: 0
});
console.log(count['Plane']);
console.log(count['Car']);
console.log(sum['Plane']);
console.log(sum['Car']);
<script src="https://cdn.jsdelivr.net/lodash/4.15.0/lodash.min.js"></script>

Nested recursive array loop

Was wondering if anyone knows of a way to use lodash, or vanilla JS to achieve this small problem?
I have this starting object:
{
"1": {
"null": {
"2": {
"3": {
"6": {
"7": "c"
},
"null": {
"null": {
"5": "b"
}
}
}
}
}
},
"8": {
"10": "e",
"null": {
"9": "d"
}
}
}
Each level (horizontally) means something. So level 1 is of type A, level 2 is of type B, 3 of type A, 4 of type B and so forth. So it alternates.
Is there a nice and simply way to "collapse" this object to look something like this:
[
{
"type": "A",
"label": "1",
"children": [
{
"type": "A",
"label": "2",
"children": [
{
"type": "B",
"label": "3",
"children": [
{
"type": "A",
"label": "6",
"children": [
{
"type": "A",
"label": "7",
"value": "c"
}
]
},
{
"type": "A",
"label": "8",
"children": [
{
"type": "A",
"label": "5",
"value": "b"
}
]
}
]
}
]
}
]
},
{
"type": "A",
"label": "8",
"children": [
{
"type": "B",
"label": "10",
"value": "e"
},
{
"type": "A",
"label": "9",
"value": "d"
}
]
}
]
In essence annotating each level with what type it is, and nesting its children.
Here is the code
function transformObj(obj, level) {
level = level || 1;
var result = _(obj).transform(function(result, value, key) {
var obj = {
type: (level % 2 === 0) ? 'B' : 'A',
label: key
};
if (key === 'null') {
result.push(transformObj(value, level+1));
} else {
if (_.isObject(value)) {
obj.children = transformObj(value, level+1);
} else {
obj.value = value;
}
result.push(obj);
}
}, [])
.flatten()
.value();
return result;
}
Here is the output
[
{
"type": "A",
"label": "1",
"children": [
{
"type": "A",
"label": "2",
"children": [
{
"type": "B",
"label": "3",
"children": [
{
"type": "A",
"label": "6",
"children": [
{
"type": "B",
"label": "7",
"value": "c"
}
]
},
{
"type": "A",
"label": "5",
"value": "b"
}
]
}
]
}
]
},
{
"type": "A",
"label": "8",
"children": [
{
"type": "B",
"label": "10",
"value": "e"
},
{
"type": "A",
"label": "9",
"value": "d"
}
]
}
]
This should do the trick:
var source = {
"1": {
"null": {
"2": {
"3": {
"6": {
"7": "c"
},
"null": {
"null": {
"5": "b"
}
}
}
}
}
},
"8": {
"10": "e",
"null": {
"9": "d"
}
}
};
function collapse(obj, parent, level){
var result = parent || [];
level = level || 0;
for(prop in obj){
var item = obj[prop];
var build = {
type : level % 2 ? "B" : "A",
label : prop
//, level : level
}
if(typeof item == 'object'){
build.children = [];
collapse(item, build.children, level + 1);
} else {
build.value = item;
}
result.push(build);
}
return result;
}
var output = collapse(source);
var result = JSON.stringify(output, null, ' ');
console.log(result);
var elem = document.getElementById("result");
elem.innerHTML = result;
<pre id="result"></pre>
function doIt(data){
return _.chain(data)
.transform(function(result, value, key){
if(key !== 'null'){
var type = _.parseInt(key) % 2 === 1 ? 'A' : 'B';
if(_.isObject(value) && !_.includes(_.keys(value), 'prop1')){
result.push({
type: type,
label: key,
children: doIt(value)
});
} else {
result.push({
type: type,
label: key,
value: value
});
}
} else {
if(_.isObject(value)){
result.push(doIt(value));
}
}
}, [])
.flatten()
.value();
}
var result = doIt(data);
result is
[
{
"type": "A",
"label": "1",
"children": [
{
"type": "B",
"label": "2",
"children": [
{
"type": "A",
"label": "3",
"children": [
{
"type": "B",
"label": "6",
"children": [
{
"type": "A",
"label": "7",
"value": "c"
}
]
},
{
"type": "A",
"label": "5",
"value": "b"
}
]
}
]
}
]
},
{
"type": "B",
"label": "8",
"children": [
{
"type": "B",
"label": "10",
"value": "e"
},
{
"type": "A",
"label": "9",
"value": "d"
}
]
}
]

Why does concurrent flattener returns only 2 inner most children

I can't figure out why this tree 'flattener' is returning only innermost children, the expectation is that it should return flattened tree.
var x = {
"Fields": {
"Id": "1",
"MasterAccountId": "",
"ParentAccountId": "",
"Name": "Name 1"
},
"Children": [{
"Fields": {
"Id": "2",
"MasterAccountId": "1",
"ParentAccountId": "1",
"Name": "Name 2"
},
"Children": [{
"Fields": {
"Id": "5",
"MasterAccountId": "1",
"ParentAccountId": "2",
"Name": "Name 5"
},
"Children": [{
"Fields": {
"Id": "6",
"MasterAccountId": "1",
"ParentAccountId": "5",
"Name": "Name 6"
}
}, {
"Fields": {
"Id": "7",
"MasterAccountId": "1",
"ParentAccountId": "5",
"Name": "Name 7"
}
}]
}]
}]
}
function recurs(n) {
console.log(n.Fields.Name);
return (n.Children != undefined ? $.map(n.Children, recurs) : n);
}
var r = recurs(x);
It returns elements with id 6, 7, while console.logs all 5 of them.
http://plnkr.co/edit/LdHiR86EDBnZFAh6aAlG?p=preview
Your function only returns n if n.Children is undefined. Since you want a flat array with all the objects you have to build one.
function recurs(n) {
var out = [];
out.push(n.Fields);
if (n.Children) {
for (var i=0, c; c = n.Children[i]; i++) {
out.push.apply(out, recurs(c));
}
}
return out;
}

Categories