Convert Flat JSON string into hierarchical using Parent ID - arrays

I am implementing a Mat-Tree using Angular Material. I have a flat JSON string like:
"Entity": [
{
"ID": 1,
"NAME": "Reports",
"PARENTID": "0",
"ISACTIVE": "Y",
"CREATIONDATE": "2020-03-31T15:08:11",
"UPDATIONDATE": "2020-03-31T15:08:11",
"CREATEDBY": 596241,
"UPDATEDBY": 596241
},
{
"ID": 2,
"NAME": "TMS - Reports",
"PARENTID": 1,
"ISACTIVE": "Y",
"CREATIONDATE": "2020-03-31T15:08:38",
"UPDATIONDATE": "2020-03-31T15:08:38",
"CREATEDBY": 596241,
"UPDATEDBY": 596241
},
{
"ID": 3,
"NAME": "TMS - Beneficiary ",
"PARENTID": 2,
"ISACTIVE": "Y",
"CREATIONDATE": "2020-03-31T15:09:34",
"UPDATIONDATE": "2020-03-31T15:09:34",
"CREATEDBY": 596241,
"UPDATEDBY": 596241
}
]
And I need to convert it into Key-value pairs based on their Parent ID. Something like:
{
Reports:
{
'Type 1 Reports': ['Beneficiary Reports', 'Some Other Report'],
'Type 2 Reports': null //No Children,
},
Some Other Menu Items: {
'My Parent Node': null,
'Some Other Menu Node': ['Child 1', 'Child 2']
}
}
So far, I am able to use this code to convert it into a parent-child hierarchy, but It has pushed all children into Items array which I cannot iterate with Mat-Tree. I need to get rid of the Items and have something like a key-value pair as the one I mentioned above:
generateTreeData(menuResponse)
{
var map = {};
for(var i = 0; i < menuResponse.length; i++){
var obj = menuResponse[i];
var parent = '';
obj.items= [];
map[obj.ID] = obj;
if(obj.PARENTID == "0")
{
parent = '-';
}
else
{
parent = obj.PARENTID;
}
if(!map[parent]){
//Means Parent doesnt exist i.e. node Itself is parent node
map[parent] = {
items: []
};
}
map[parent].items.push(obj);
}
return map['-'].items;
}
Problem:
The code puts children nodes in Items array. I need to get rid of the Items array and place it in key-value pairs like the one I mentioned above. How do I just extract the "NAME" and Items out of this JSON array and make a Key-Value pair? Something like the one I mentioned above?

The expected output structure doesn't seem right. What if there is a child for 'Beneficiary Reports' node.
The output structure for the input provided in the question should be like:
{
"Reports":[
{
"TMS - Reports":[
{
"TMS - Beneficiary ":[]
}
]
}
]
}
Check the following fiddle: https://jsfiddle.net/abby_7700/apkgeo1d/3/
var data = [];//your response goes here
var result = {};
var root = data.find(x => x.PARENTID == 0);
result[root.NAME] = [];
findAndAddChildren(root.ID, result[root.NAME]);
console.log(JSON.stringify(result));
function findAndAddChildren(parentID, collection) {
var rootChildren = data.filter(x => x.PARENTID == parentID);
for(var i = 0; i < rootChildren.length; i++) {
var child = rootChildren[i];
var temp = {};
temp[child.NAME] = [];
collection.push(temp);
findAndAddChildren(child.ID, temp[child.NAME]);
}
}

Related

Karate API framework is there any way to iterate through the child values

I am getting 90 cent success with my approach but when the response has got multiple entries in one of the child key then the logic gets failed and I am not able to put one common logic in place which would run for all the cases.
The response sample is
{
"items": [
{
"id":1,
"name": "John",
"sections": [
{
"id":1,
"description": "John smith"
}
]
}
]
}
Now my use case says you search for John text and then items array would contains many objects whose items.name or items.sections.description should contains the "John" keyword
The matching logic which I have put is working fine because I am iterating through items[].name and items.sections[].description
The main challenge comes when the sections[*].description contains the multiple sections like below
{
"items": [
{
"id":1,
"name": "John",
"sections": [
{
"id":1,
"description": "John smith"
},
{
"id":1,
"description": "remain smith of the first object"
}
]
}
]
}
The logic should now work on
items[].name or items.sections[].description (multiple entries of sections[*].description)
Problem I am facing is when I am iterating items[].name & items[].sections[*].description
It gives me all the names and all the sections.description in separate arrays what I want is that it should give me one by one.
For example first result set should give me below
[
"John"
]
and
[
"John smith"
"remain smith of the first object"
]
So that I can run my existing logic to check whether John is available or not. Currently my logic runs on first entry of the description and it does not check the next entry or section.description that is the reason below matching object is failing because "john" is present in the second entry of the description
{
"items": [
{
"id":11,
"name": "SMITH",
"sections": [
{
"id":11,
"description": "SMITH"
},
{
"id":11,
"description": "JOHN Carter"
}
]
}
]
}
The matching logic which I am currently using is -
* def matchText =
"""
function (nameArr, sectionArr, matchingWord)
{
for(var i = 0; i < nameArr.length; i++)
var regEx = new RegExp(matchingWord, 'gi')
var nameMatch = nameArr[i].match(regEx)
var secMatch = sectionArr[i].match(regEx)
if (nameMatch ==null && secMatch == null) {
return false;
}
}
return true;
}
"""
* def getName = get response.items[*].name
* def getDescription = get response.items[*].sections[*].description
* assert matchText(getName,getDescription,'john')
So this logic works when you have same length in name & sections.description but when sections.description has got multiple arrays then it does not iterate correctly. That was the only reason I wanted to treat the name as one object and sections.description as another even when there will be multiple child ids in it.
Sample Code:
Feature: Validation
Scenario:
* def resp =
"""
{
"items": [
{
"id": 11,
"name": "JOHN",
"sections": [
{
"id": 11,
"description": "SMITH"
},
{
"id": 11,
"description": "JOHN Carter"
}
]
}
]
}
"""
* def names = []
* def fun =
"""
function(x){
var json = x.sections;
var temp = x.name
for(var i = 0; i < json.length; i++) {
var obj = json[i];
temp = temp + "," + obj.description
}
karate.appendTo(names, temp);
}
"""
* def items = get resp.items[*]
* karate.forEach(items, fun)
* match each names == '#regex .*JOHN.*'

How to filter following JSON with ionic filter bar

Using Ionic Filter Bar for implementing search in ionic app, I need to to search on "parent_type" and "child_type" from following json,
{
"status": {
"code": 200,
"message": "all data grabbed successfully",
"cause": ""
},
"ParentData": [
{
"id": 67,
"parent_type": "Parent Type",
"child": [
{
"id": 86,
"child_type": "Child Type"
},
{
"id": 93,
"child_type": "Child Type1"
}
]
},
{
"id": 68,
"parent_type": "Parent Type 1",
"child": [
{
"id": 87,
"child_type": "Child Type 1"
},
{
"id": 94,
"child_type": "Child Type 2"
}
]
}
]
}
its working fine for "parent_type" with following code
Here is the filter code
$scope.showFilterBar = function () {
filterBarInstance = $ionicFilterBar.show({
//setting parentData in following way after rest service call
//$scope.parentData = jsondata.ParentData
items: $scope.parentData,
update: function (filteredItems, filterText) {
$scope.parentData = filteredItems;
if (filterText) {
console.log(filterText);
}
},
filterProperties: ['parent_type']
});
};
But not able to search on "child_type"
So is there any way to set filterProperties so it will work for both "parent_type" and "child_type" or any other way to search this Json?
Thanks.
You should remove the "filterProperties" key and then add "expression" key.
Your "filterBarInstance" will be something like this:
$scope.showFilterBar = function () {
filterBarInstance = $ionicFilterBar.show({
items: $scope.parentData,
update: function (filteredItems, filterText) {
$scope.parentData = filteredItems;
if (filterText) {
console.log(filterText);
}
},
expression: function(filterText,value,index,array){
// This function is called for every ParentData object.
// "value" will be the current ParentData object,
// you'll need to iterate over all the "child" array in order to
// check if any of the "child_type" keys contains the "filterText"
// so if "parent_type" contains the "filterText" and any(? or all)
// of the the "child[x].child_type" values also contains the
// "filterText" return true
}
});
};
Check https://github.com/djett41/ionic-filter-bar for the original documentation.
Sorry for my english :)

JavaScript (Node.js) - JSON recursion extracting objects to array with order (faltten)

I have a JSON config file as follows:
var conf = [
{
"value": "baz",
"threshold": 20,
"other": 123
},
{
"value": "mo",
"other": 456,
"child": {
"value": "foo",
"other": 789,
"child": {
"value": "larry",
"other": 123
}
}
}
];
I have a requirement to extract each of the objects and persist them together in order if they have child objects. For example, object 1 (baz) is stand alone. Object 2 (mo) will have two child objects. These 3 as a set must be extracted together.
There is no limit to the number of child objects.
Im attempting to persist each object using an array to maintain the order. So the required output would look like:
[[{"value":"baz","threshold":20,"other":123}],
[[{"value":"mo","other":456,"child":{"value":"foo","other":789,"child":{"value":"larry","other":123}}}],
[{"value":"foo","other":789,"child":{"value":"larry","other":123}}],
[{"value":"larry","other":123}]]]
A final requirement is to actually remove the child values from the parents so the output can actually be like:
[
[{"value":"baz","threshold":20,"other":123}],
[
[{"value":"mo","other":456}],
[{"value":"foo","other":789}],
[{"value":"larry","other":123}]
]
]
Ive been hung up on this for hours with little progress. I know I need to create a recursive function, push each node to an array, and then check for child object and repeat.
Heres what I have so far. My thinking is if I can take the array id each task is being pushed to (using a loop id), perhaps I can map that when the function is called again.
Appreciate any guidance.
var execSets = [];
function parser(tasks){
// an ordered array of task execution
for (let eachTask in tasks) {
var taskSet = [];
console.log("====================================");
console.log(tasks[eachTask]);
if(!tasks[eachTask].child && typeof(tasks[eachTask]) === 'object'){
console.log(tasks[eachTask]);
taskSet.push(tasks[eachTask]);
execSets.push(taskSet);
}
if(tasks[eachTask].child){
let childAlias = tasks[eachTask].child;
delete tasks[eachTask].child;
taskSet.push(tasks[eachTask]);
execSets.push(taskSet);
parser(childAlias);
}
}
}
The npm registry is your friend; try 'npm search flat,
There are a few modules that can help flatten a json object. For example https://www.npmjs.com/package/flat
You could do it using recursion. Here is my suggestion:
var conf = [
{
"value": "baz",
"threshold": 20,
"other": 123
},
{
"value": "mo",
"other": 456,
"child": {
"value": "foo",
"other": 789,
"child": {
"value": "larry",
"other": 123
}
}
}
];
function getFlattenedObject(object){
var response = [];
flatten(object, response, 0);
return response;
}
function flatten(object, array, index){
if(!array[index]){
array.push([]);
}
array[index].push(object);
if(object.child){
flatten(object.child, array, index + 1);
object.child = undefined;
}
}
//Logs for comparison
console.dir(conf)
console.dir(getFlattenedObject(conf));
The result structure you are looking for wasn't "intuitive" and hence the solution has gotten a little ugly, but here is how you could use object-scan to answer your question
// const objectScan = require('object-scan');
const data = [{"value":"baz","threshold":20,"other":123},{"value":"mo","other":456,"child":{"value":"foo","other":789,"child":{"value":"larry","other":123}}}]
const fn = (haystack) => objectScan(['[*]', '**.child'], {
filterFn: ({
key: [id, ...p],
value: { child, ...node },
context
}) => {
if (!(id in context)) {
context[id] = [];
}
context[id].push(child || p.length !== 0 ? [node] : node);
}
})(haystack, []);
console.log(fn(data));
// => [ [ { value: 'baz', threshold: 20, other: 123 } ], [ [ { value: 'larry', other: 123 } ], [ { value: 'foo', other: 789 } ], [ { value: 'mo', other: 456 } ] ] ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.7.1"></script>
Disclaimer: I'm the author of object-scan

angularJS scope orderBy does not sort children, just the parent

When I used
$scope.data = $filter('orderBy')(data,'title');
the parent is sorted, the children are not.
What if my array has children? how can I order the parent and the children?
My array:
data = [
{
"id": 1,
"title": "abc",
"items": [
{
"id": 3,
"title": "abc2",
"items": []
},
{
"id": 4,
"title": "abc1",
"items": []
}
]
},
{
"id": 2,
"title": "cde",
"items": [
{
"id": 5,
"title": "cde2",
"items": []
},
{
"id": 6,
"title": "cde1",
"items": []
}
]
}
]
Thanks in advance.
It shouldn't sort anything within the child's objects.
For sorting within child object sort your 'items' recursively, for each child, with $filter('orderBy') or similar with code bellow use your own custom comparing function.
var orderByFieldName = 'title',
childPropertyForSorting = 'items',
s = function (a, b) {
//Revise comparing function depends on direction of sorting and your wishes
if (a[orderByFieldName] > b[orderByFieldName])
return 1;
if (a[orderByFieldName] < b[orderByFieldName])
return -1;
return 0;
},
f = function (o) {
if (o.hasOwnProperty(childPropertyForSorting) && o[childPropertyForSorting] instanceof Array) {
o[childPropertyForSorting].sort(s);
o[childPropertyForSorting].forEach(f);
}
};
if (originalData instanceof Array) {
originalData.sort(s);
originalData.forEach(f);
}
I would suggest if you are only trying to orderBy in the view to use
data-ng-repeat="item in data | orderBy: 'colum you want'"
Read more about it here
Reason is the children is in another level and treated just as another key value pair inside that object, you can use the same filter you used inside a for loop that will loop each data array item and sort the children like this :
//filter the parent level by title
$scope.data = $filter('orderBy')(data,'title');
//copy the sorted array in temp variable
var temp = angular.copy($scope.data);
//sort the children by title while aggregating each array item and then storing the output in $scope.
for(var i=0;i<data.length;i++){
$scope.data[i].items = $filter('orderBy')(temp[i].items,'title');
}

Underscore JS to retrieve object by ID

I have a input where I want to find an object by ID. At the moment I am returning both objects but what I want is if I search '01' I would just return the first object. I have tried underscore _.map to do this but it did not give the result I am after.
var getById = function() {
var deferred = Q.defer(),
result;
result = items;
if (!result) {
deferred.reject('item not found');
} else {
deferred.resolve(result);
}
return deferred.promise;
};
JSON:
[{
"id": "01",
"name": "test1",
"orderItems": [
{
"productNumber": "TESTa",
"quantity": 2,
},
{
"productNumber": "TESTb",
"quantity": 4,
},
{
"productNumber": "TESTc",
"quantity": 6,
}
]
},{
"id": "02",
"name": "test2",
"orderItems": [
{
"productNumber": "TESTe",
"quantity": 2,
},
{
"productNumber": "TESTf",
"quantity": 7,
},
{
"productNumber": "TESTg",
"quantity": 6,
}
]
}]
You can use _.filter()
Looks through each value in the list, returning an array of all the values that pass a truth test (predicate).
result = _.filter(items, function(item){
return item.id == '01';
});
Or, _.findWhere
Looks through the list and returns the first value that matches all of the key-value pairs listed in properties.
result = _.findWhere(items, {id : '01'});
var result = _.find(myItems, function(item) { return item.id === '01'; }
If you find single item which matches the conditions, use _.find()
It looks through each value in the list, returning the first one that
passes a truth test
var _exist = _.find(_items, function (item) {
return item.id == your_id;
});
If you find all items which matches the conditions, use _.filter()
It looks through each value in the list, returning an array of all the
values that pass a truth test
var _exist = _.filter(_items, function (item) {
return item.id == your_id;
});
Catch the complete documentation here:
http://underscorejs.org/

Resources