JSON printing objects value recursively - json

I'm trying to build a tree using JSON for which i'm trying to just print in browser console.
var json = [{
name: "1",
id: 1,
child: [{
name: "11",
id: 11,
child: [{
name: "111",
id: 111
}, {
name: "112",
id: 112
}]
}, {
name: "12",
id: 12
}]
}];
function ya(obj) {
console.log(obj.name);
if (obj.child) {
console.log("length=" + obj.child.length);
for (i = 0; i < obj.child.length; i++) {
ya(obj.child[i]);
}
}
}
ya(JSON.parse(JSON.stringify(json))[0]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Here I would like to print
1
11
111
112
12
in console. But it only prints
1
11
111
112
I want to do this recursively. How can I achieve this?

You need to declare i with var:
for (var i = 0; i < obj.child.length; i++)
See Difference between variable declaration syntaxes in Javascript (including global variables)? for more details.

Related

How to filter JSON data by URL parameter?

Lets say if I am looking to filter by id=2 in the browser's URL. This is currently not working (retrieving all the data instead):
/stories?id=2
This is my json file:
{
"stories": [
{
"id": 0,
"title": "Puss In Boots",
"subtitle": ""
},
{
"id": 1,
"title": "Aladdin",
"subtitle": ""
},
{
"id": 2,
"title": "Sleeping Beauty",
"subtitle": ""
},
{
"id": 3,
"title": "Puppy Dreams",
"subtitle": ""
}
]
}
(if you need vanilla js only, here is the solution)
I create a simple function that gets the ID from the URL, then access the correct id in JSON, and shows the title inside a div.
change the ?id= to see the changes
const data = {
stories: [{
id: 0,
title: "Puss In Boots",
subtitle: "",
},
{
id: 1,
title: "Aladdin",
subtitle: "",
},
{
id: 2,
title: "Sleeping Beauty",
subtitle: "",
},
{
id: 3,
title: "Puppy Dreams",
subtitle: "",
},
],
};
const card = document.getElementById("card");
setText(data, card);
function setText(data, element = document.body) { // element if there isn't then we get the body instead
const id = getUrlParams().id ?? 0; // if url is ?id=2 then it will return 2, if undefined then 0
const base = data.stories[id]; // change this if the structure is different or changed.
myAppLogic();
function myAppLogic() { // put here all your logic
element.textContent = base.title;
}
}
function getUrlParams() {
let result = []; // [] empty array at the start
const stringArray =
location.search // "?id=number&other=string&another=string" , this get the last part of the URL
.substring(1) // delete the "?" character from the location.search
.split("&"); // ["id=number", "other=string", "another=string"]
stringArray.forEach((string) => {
result.push(
string.split("=")
);
}); // [[id, number], [other, string], [another, string]]
return Object.fromEntries(result); // {id: number, other: string, another: string}
}
#card {
background: #ccc;
padding: 1rem;
border-radius: 0.5rem;
text-align: center;
}
<div id="card"></div>

How to group suggestions nested list in the primeng autocomplete angular 8

I am trying to group the autocomplete suggestions and would like to render them in primeng.
How we can add a custom template in primeng?
my data
data = [{"id":"m1","name":"menu1","val":"D","items":[{"id":"d1","name":"datanested1","val":"D","items":[{"id":"1","name":"direct Data","val":"E"},{"id":"2","name":"test","val":"E"}]}]},{"id":"d2","name":"menu2","val":"D","items":[{"id":"21","name":"test21","val":"E"},{"id":"22","name":"test23","val":"E"}]},{"id":"d3","name":"menu3","val":"D","items":[{"id":"31","name":"test data 3","val":"E"},{"id":"32","name":"test data 4","val":"E"}]}]
Is there any other libraries available in angular 8 which support this?
I would like to achieve something like this when users start searching in autocomplete...
Menu1 - header
datanested1 -subheader
direct Data -values
test -values
Menu2 - header
test21-values
test23-values
Menu3 - header
test data 3-values
test data 4-values
1. if the user types "direct" in the input box...
Menu1 - header
datanested1 -subheader
direct Data -values
2. if the user types "data" in the input box...
Menu3 - header
test data 3-values
test data 4-values
3. if the user types "menu" in the input box...
Menu1 - header
datanested1 -subheader
direct Data -values
test -values
Menu2 - header
test21-values
test23-values
Menu3 - header
test data 3-values
test data 4-values
I have tried the following example in stackblitz.
https://stackblitz.com/edit/primeng-7-1-2-qtsnpm
In the below approach we will reduce the array to a simple structure
[
{
"id": "m1",
"name": "menu1",
"val": "D",
"search": ["m1", "d1", "1", "2", "menu1", "datanested1", "direct Data", "test"],
"depth": 2
},
{
"id": "d1",
"name": "datanested1",
"val": "D",
"search": ["d1", "1", "2", "datanested1", "direct Data", "test"],
"depth": 1
},
{
"id": "1",
"name": "direct Data",
"val": "E",
"search": ["1", "direct Data"
],
"depth": 0
},
...
]
The idea is this, we will use the depth to format out text while the search for searching.
maxDepth = 0;
reducedArray = (arr, depth = 0, parentSearch = []) => {
this.maxDepth = Math.max(this.maxDepth, depth);
if (!arr) {
return [];
}
return arr.reduce((prev, { items, ...otherProps }) => {
// const depth = this.findDepth({ items, ...otherProps });
const search = [
...this.getProps({ items, ...otherProps }, "id"),
...this.getProps({ items, ...otherProps }, "name")
];
const newParentSearch = [...parentSearch, otherProps.name, otherProps.id];
return [
...prev,
{ ...otherProps, search, depth, parentSearch },
...this.reducedArray(items, depth + 1, newParentSearch)
];
}, []);
};
getProps = (item, prop) => {
if (!item.items) {
return [item[prop]];
} else {
return [
item[prop],
...item.items.map(x => this.getProps(x, prop))
].flat();
}
};
We can apply styles like below
getStyle(depth) {
return {
color: depth === 0 ? "darkblue" : depth === 1 ? "green" : "black",
paddingLeft: depth * 7 + "px",
fontWeight: 800 - depth * 200
};
}
I will use reactive programming so I will convert the object to an Observable usinf of operator
data$ = of(this.reducedArray(this.data));
filteredData$ = combineLatest([this.data$, this.filterString$]).pipe(
map(([data, filterString]) =>
data.filter(
({ search, parentSearch }) =>
!![...search, ...parentSearch].find(x => x.includes(filterString))
)
)
);
We now done, the remaining is to update html
<p-autoComplete [(ngModel)]="cdsidvalue" [suggestions]="filteredData$ | async"
(completeMethod)="filterString$.next($event.query)" field="name" [size]="16" placeholder="Menu" [minLength]="1">
<ng-template let-menu pTemplate="item">
<span
[ngStyle]="getStyle(menu.depth)" >{{ menu.name }}</span>
</ng-template>
</p-autoComplete>
Demo Here
Update
Since we are using reactive programming, refactoring to accept http requests is quite easy, simply replace the observable with an http request
data$ = this.http.get<any[]>("my/api/url");
filteredData$ = combineLatest([this.data$, this.filterString$]).pipe(
map(([data, filterString]) =>
this.reducedArray(data).filter(
({ search, parentSearch }) =>
!![...search, ...parentSearch].find(x => x.includes(filterString))
)
)
);
See this update in action
I have also updated the html to add loading message, we do not want to display a form without data
Patel , Yes you can add Group Header in NgPrime
.ts
adminentrylistSearch = [
{
Grp_Header:'THIS IS Header 1' ,
cdsid: "0121",
firstname: "FirstName1",
lastname: "LastName1",
fullname: "LastName1, FirstName1"
},
{
cdsid: "0122",
firstname: "FirstName1",
lastname: "LastName2",
fullname: "LastName2, FirstName2"
},
{
cdsid: "0123",
firstname: "FirstName3",
lastname: "LastName3",
fullname: "LastName3, FirstName3"
},
{
Grp_Header:'THIS IS Header 2',
cdsid: "0124",
firstname: "FirstName4",
lastname: "LastName4",
fullname: "LastName4, FirstName4"
},
{
cdsid: "0125",
firstname: "FirstName5",
lastname: "LastName5",
fullname: "LastName5, FirstName5"
},
{
cdsid: "0126",
firstname: "FirstName6",
lastname: "LastName6",
fullname: "LastName6, FirstName6"
},
{
cdsid: "0127",
firstname: "FirstName7",
lastname: "LastName7",
fullname: "LastName7, FirstName7"
}
];
.html
<p-autoComplete [(ngModel)]="cdsidvalue" [suggestions]="filteredCountriesSingle" [dropdown]="true"
(completeMethod)="filterCountrySingle($event)" field="firstname" [size]="16" placeholder="CDSID">
<ng-template let-adminentrylistSearch [ngIf]="adminentrylistSearch.index" pTemplate="text">
<div class='unclickable-header'>
<span [style.font-weight]="adminentrylistSearch.Grp_Header ? 'bold' : null"> {{adminentrylistSearch.Grp_Header ? adminentrylistSearch.Grp_Header : adminentrylistSearch.firstname}} </span>
</div>
</ng-template>
</p-autoComplete>
Check Result here...
Demo
In your primeng version (7.1.2) grouped autocomplete is not supported. But it's supported in latest version(11.3.0). Please see below image and follow https://www.primefaces.org/primeng/showcase/#/autocomplete

Convert Form Data Array Object to JSON [duplicate]

This question already has answers here:
Convert JS object to JSON string
(23 answers)
Closed 1 year ago.
I'm trying to convert a form data object to JSON
var dataFormItin = $("#formitinerary").serializeArray();
result
itinerary: {
'itinerary[0][date]': undefined,
'itinerary[0][title]': 'Day 1 to Colombo',
'itinerary[0][destinationId]': '5ff3b8d7f0f3bf04b8141362',
'itinerary[0][program]': 'asd',
'itinerary[0][transfer_duration]': '2 hours'
}
and i want to make it like
itinerary : [
{ date : ..., title :...},
{ date : ..., title :...}
]
Maybe, it will be solved your problem
// 1. Example Data
const serializeArray = [
{ name: "itinerary[0][date]", value: "" },
{ name: "itinerary[0][title]", value: "Day 1 to Colombo" },
{ name: "itinerary[0][destinationId]", value: "5ff3b8d7f0f3bf04b8141362" },
{ name: "itinerary[0][program]", value: "asd" },
{ name: "itinerary[1][date]", value: "" },
{ name: "itinerary[1][title]", value: "Day 1 to Colombo" },
{ name: "itinerary[1][destinationId]", value: "5ff3b8d7f0f3bf04b8141362" },
{ name: "itinerary[1][program]", value: "asd" },
]
// 2. Define object key here
const arrayOfKey = ['date', 'title', 'destinationId', 'program']
// 3. Create empty array object
const arrayObject = []
// 4. Transform Serialize Array into Array Object
for(i = 0; i < serializeArray.length / arrayOfKey.length; i++ ){
const newObject = {}
for(const key of arrayOfKey){
newObject[key] = (serializeArray.find(data => data.name == `itinerary[${i}][${key}]`)).value
}
arrayObject.push(newObject)
}
// 5. Show the result
console.log(arrayObject)
/**
* [
{
date: '',
title: 'Day 1 to Colombo',
destinationId: '5ff3b8d7f0f3bf04b8141362',
program: 'asd'
},
{
date: '',
title: 'Day 1 to Colombo',
destinationId: '5ff3b8d7f0f3bf04b8141362',
program: 'asd'
}
]
*/

Echarts - Dynamic multi line chart based on Json data

I am trying to plot time line chart with multiple lines (number of lines is dynamic based on Json data). I am getting Json data as mentioned. I would like to have each line plotted for each product. How should I convert data so that echarts supports?
[
{date: "2019-05-21 00:00:00 UTC", value: 100, product: 'p1'},
{date: "2019-05-21 00:00:00 UTC", value: 50, product: 'p2'},
{date: "2019-05-19 00:00:00 UTC", value: 200, product: 'p3'},
{date: "2019-05-18 00:00:00 UTC", value: 70, product: 'p1'},
{date: "2019-05-18 00:00:00 UTC", value: 125, product: 'p2'},
{date: "2019-05-18 00:00:00 UTC", value: 55, product: 'p3'}
]
You should be storing your axis data separately from your values, in a simple multi-line chart: see the following two examples:
https://echarts.baidu.com/echarts2/doc/example/line1.html#-en
https://echarts.baidu.com/echarts2/doc/example/line8.html#-en
The second example provides the "time" x-axis type, which we can use with the first, which provides a good multi-line framework.
Your option may look something like this:
option = {
title : {
text: 'Multi-line Time Axis example',
subtext: 'made for Dileep'
},
tooltip : {
trigger: 'axis'
},
toolbox: {
show : true,
feature : {
mark : {show: true},
dataView : {show: true, readOnly: false},
magicType : {show: true, type: ['line', 'bar']},
restore : {show: true},
saveAsImage : {show: true}
}
},
calculable : true,
xAxis : [
{
type : 'time',
}
],
yAxis : [
{
type : 'value',
axisLabel : {
formatter: '{value} $'
}
}
],
series : [
{
name: 'series1', // Product p1, for instance
type: 'line',
data: (function () {
var d = [];
var len = 0;
var now = new Date();
var value;
while (len++ < 10) {
d.push([
new Date(2014, 9, 1, 0, len * 10000), // some Date() object
(Math.random()*30).toFixed(2) - 0// // some random value
]);
}
return d;
})()
},
{
name: 'series2', // product p2
type: 'line',
data: (function () {
var d = [];
var len = 0;
var now = new Date();
var value;
while (len++ < 10) {
d.push([
new Date(2014, 9, 1, 0, len * 10000),
(Math.random()*30).toFixed(2) - 0,
]);
}
return d;
})()
}
]
};

Nested arrays in object json to csv

Im using 'json-csv' library to create a csv from a users arrays with nested objects and arrays.
var users = [
{
subscriptions: [
{
package : {
name: 'Grammar'
},
state: 'EXPIRED',
timerange: {
period : 5550
},
transaction:{
amount: 10000
}
},
{
package : {
name: 'GK'
},
state: 'ACTIVE',
timerange: {
period : 30
},
transaction:{
amount: 10340
}
},
],
account:{
balance: 200
},
name: "Johhy Moe",
email: null,
user_id: "123456789",
username: null,
user_type: "facebook",
id: 3,
createdAt: "2016-07-11T08:02:40.000Z",
updatedAt: "2016-07-11T08:02:40.000Z",
},
{
subscriptions: [
{
package : {
name: 'GK'
},
state: 'EXPIRED',
timerange: {
period : 42
},
transaction:{
amount: 5252
}
},
{
package : {
name: 'MATH'
},
state: 'ACTIVE',
timerange: {
period : 25
},
transaction:{
amount: 200
}
}
],
account:{
balance: 1500
},
name: "John Doe",
email: null,
user_id: "123456789",
username: null,
user_type: "facebook",
id: 7,
createdAt: "2016-07-29T06:44:18.000Z",
updatedAt: "2016-07-29T06:44:18.000Z"
},
]
Now i want the generated csv to be like this
USERID,NAME,FBID,ACCOUNT,SUBSCRIPTION,PRICE,STATE,TIMEPERIOD
3,Johhy Moe,123456789,200,Grammar,10000,EXPIRED,5550
3,Johhy Moe,123456789,200,GK,10340,ACTIVE,30
7,John Doe,123456789,1500,GK,5252,EXPIRED,30
7,John Doe,123456789,1500,MATH,200,ACTIVE,25
As you see if there are two objects inside subscription array for each user, i want to repeat that user again but with different subscription data.
I've thought of using the library because my users array can go up to thousands of users with hundreds of subscription.
And i'm at a loss to what i should do.
my Code:
var options= {
fields : [
{
name : 'id',
label : 'USERID'
},
{
name : 'name',
label : 'Name'
},
{
name : 'user_id',
label : 'FBID'
},
{
name : 'account.balance',
label : 'ACCOUNT'
},
{
name: '',
label: 'Subscription'
}
]
}
var source = es.readArray(users)
source
.pipe(jsoncsv.csv(options))
.pipe(res)
I dont want to use a library also. So if someone could provide me with a resource to make my own csv file with strings and also using streams , that would be great. Thanks!!
This will solve your problem. Now you just have to change console.log to fs and write to your file.
var json2csv = function (json, listKeys) {
var str = "";
var prefix = "";
for (var i = 0; i < listKeys.length; i++) {
str += prefix + json[listKeys[i]];
prefix = ",";
}
return str;
};
var async = require('async');
var csvData = ['USERID,NAME,FBID,ACCOUNT,SUBSCRIPTION,PRICE,STATE,TIMEPERIOD'];
async.each(users, function (user, callback) {
var csvRow1 = {
USERID: user.id,
NAME: user.name,
FBID: user.user_id,
ACCOUNT: user.account.balance
};
async.each(user.subscriptions, function (subscription, callback) {
var csvRow2 = JSON.parse(JSON.stringify(csvRow1));
csvRow2.SUBSCRIPTION = subscription.package.name;
csvRow2.PRICE = subscription.transaction.amount;
csvRow2.STATE = subscription.state;
csvRow2.TIMEPERIOD = subscription.timerange.period;
csvData.push(json2csv(csvRow2, ['USERID', 'NAME', 'FBID', 'ACCOUNT', 'SUBSCRIPTION', 'PRICE', 'STATE', 'TIMEPERIOD']));
callback(null);
}, function (err) {
callback(err);
});
}, function (err) {
if (err) {
// return err;
} else {
// return csvData;
}
});