I have the following class that gets sent as an IEnumerable from an API:
public class LogsDto
{
public decimal Value { get; set; }
public DateTime Time_Stamp { get; set; }
public string TagName { get; set; }
}
This is the Angular class that the data is passed into as an array:
export class Logs {
value: number;
timestamp: string;
tagName: string;
}
Sample data will come across as so:
{ "value": 100, "time_Stamp": "2017-05-04T00:07:47.407", "tagName": "Tag 1" },
{ "value": 200, "time_Stamp": "2017-05-04T00:07:47.407", "tagName": "Tag 2" },
{ "value": 300, "time_Stamp": "2017-05-04T00:07:47.407", "tagName": "Tag 3" },
{ "value": 150, "time_Stamp": "2017-05-04T00:07:57.407", "tagName": "Tag 1" },
{ "value": 250, "time_Stamp": "2017-05-04T00:07:57.407", "tagName": "Tag 2" },
{ "value": 350, "time_Stamp": "2017-05-04T00:07:57.407", "tagName": "Tag 3" }
In Angular, I want to turn this into a table that is read like so:
<table>
<th>Time_Stamp</th> <th>Tag 1</th> <th>Tag 2</th> <th>Tag 3</th>
<td>2017-05-04T00:07:47.407</td> <td>100</td> <td>200</td> <td>300</td>
<td>2017-05-04T00:07:45.407</td> <td>150</td> <td>250</td> <td>350</td>
</table>
I can hard code it fine as shown above. The problem I am having is I don't know how to parse the data from columns to rows. The "tagName" property will not always be the same. Sometimes there will be 10 different tag names.
Any help is appreciated.
This is how I ended up making it work. It is pretty fast, but I am sure I am not doing it the most efficient way. Would appreciate constructive criticism.
parseLogs(logs: Logs[]): void {
const ts: string[] = logs.map(data => data.time_Stamp.toString());
var timestamps = ts.filter((x, i, a) => x !== undefined && a.indexOf(x) === i);
var jsonString: string = '[';
for (var j = 0; j < timestamps.length; j++) {
var date = new Date(timestamps[j]);
var hours = date.getHours().toString();
if (date.getHours() < 10)
hours = '0' + hours;
var minutes = date.getMinutes().toString();
if (date.getMinutes() < 10)
minutes = '0' + minutes;
var dtString: string = (date.getMonth() + 1) + '-' + date.getDate() + '-' + date.getFullYear() + ' ' + date.getHours() + ':' + date.getMinutes();
jsonString = jsonString + '{"Time Stamp":"' + dtString + '"';
for (var i = 0; i < logs.length; i++) {
if (logs[i].time_Stamp === timestamps[j])
jsonString = jsonString + ',"' + logs[i].tagName + '":' + logs[i].value + '';
}
if (j === (timestamps.length - 1)) {
console.log('j:' + j + 'logs.length:' + logs.length.toString());
jsonString = jsonString + '}';
} else {
console.log('j:' + j + 'logs.length:' + logs.length.toString());
jsonString = jsonString + '},';
}
}
jsonString = jsonString + ']';
console.log(jsonString);
this.myLogs = JSON.parse(jsonString);
//From example shown above
this.generateColumnHeaders(this.myLogs);
A quick way to solve it would be to find all the unique properties off the objects being returned through reflection and store them. Then you could render out a header, then render each object and access via the columns in a nested set of ngFors. This, of course, requires the ordering to remain the same as you evaluate over the columns, incidentally. You'll also want to perform a projection of your data to perform something like a pivot. Then we will perform a group by of the data by timestamp and for that we will 100% steal someone else's SO group by function.
The control order is pivot then column header eval.
The column eval:
generateColumnHeaders(myObjectArray) {
this.columns = [];
for(var i = 0; i < myObjectArray.length; ++i) {
for(var col in myObjectArray[i]) {
if(myObjectArray[i].hasOwnProperty(col)) {
if(this.columns.findIndex(colmn => colmn === col) === -1) {
this.columns.push(col);
}
}
}
}
}
The pivot and other transforms:
transformArray(objectArray, accessObject) {
return this.groupByArray(objectArray.map(obj =>
{
var tagNameString = obj.tagName;
let tempObj = {};
tempObj[accessObject.pivotColumn] = obj.time_Stamp;
tempObj[obj.tagName] = obj.value;
return tempObj;
}), accessObject.pivotColumn).map(tsg => {
let tempObj = {};
tempObj[accessObject.pivotColumn] = tsg.time_Stamp;
for(var i = 0; i < tsg.values.length; ++i) {
for(var tag in tsg.values[i]) {
if(tag.indexOf(accessObject.dynamicColumnCommon !== -1)) {
tempObj[tag] = tsg.values[i][tag];
}
}
}
return tempObj;
});
}
Controlling code:
this.myObjectArray = this.transformArray(this.myObjectArray, { pivotColumn: "time_Stamp", dynamicColumnCommon:"Tag"});
this.generateColumnHeaders(this.myObjectArray);
The template evaluation:
<table>
<tr>
<th *ngFor="let column of columns">{{column}}</th>
</tr>
<tr *ngFor="let obj of myObjectArray">
<td *ngFor="let column of columns">{{obj[column]}}</td>
</tr>
</table>
Plunker
Related
I am working in angular and trying to format an API response into a simple HTML table in Angular, this process is generally straightforward when the data is single level eg.
{
property-1: value-1
property-2: value-2
.
.
.
property-n: value-n
}
However my array of Objects looks something like this:
[
index: index_1
value: {
property-1: value-1
property-2: value-2
property-3: value-3
},
index: index_1
value: {
property-1: value-1
property-2: value-2
property-3: value-3
},
index : index_2
value : {
property-1: value-1
property-2: value-2
property-3: value-3
property-4: value-4
}
]
What I am hoping to achieve is one table for each index, so index_1 would get one table with column names matching the keys for index_1 (they are always going to be the same keys when the index is the same) and table data being all the data from the different objects in side the main object (if that makes sense)
Gap in Understanding
Where I am having trouble is that the format of the API response is a little weird in that index_1 can show up for as many times as a result for index_1 is spit out, so I don't think I can use a simple ngFor, now there may be a better way for me to format my data from the API response, at the moment this is how I am going about it:
let results: any[][] = await this.searchAllIndexes(searchTerm);
results.forEach(i => i.forEach(j => {
let json = {
index: j._index,
value: j._source //j._source is an object itself
}
result.push(json) //result is a local array of objects inside the function
}))
this.resultObject = result; //this.result is an array of objects
Below is a console log of my output for reference:
Data formatting
const data = [
{
index: "new_index",
value: {
PLN_TYP_CD: "S",
PLN_TYP_DESC: "Disability",
INSRT_BTCH_ID: 4033598,
LST_UPDT_BTCH_ID: -1,
INSRT_TS: "2017-12-06T11:25:47.891258",
},
},
{
index: "custom_index_5",
value: {
PROV_ASGN_ROLE_TYP_CD: "S",
PROV_ASGN_ROLE_TYP_DESC: "PHYSICIAN IS A SPECIALIST",
POL_ORIG_SRC_SYS_CD: "NDB",
PROV_ASGN_ROLE_TYP_NRML_CD: "S",
},
},
{
index: "custom_index_5",
value: {
PROV_ASGN_ROLE_TYP_CD: "S",
PROV_ASGN_ROLE_TYP_DESC: "SPECIALIST",
POL_ORIG_SRC_SYS_CD: "CRR",
PROV_ASGN_ROLE_TYP_NRML_CD: "S",
},
},
{
index: "custom_index_5",
value: {
PROV_ASGN_ROLE_TYP_CD: "S",
PROV_ASGN_ROLE_TYP_DESC: "PHYSICIAN IS A SPECIALIST",
POL_ORIG_SRC_SYS_CD: "OXF",
PROV_ASGN_ROLE_TYP_NRML_CD: "S",
},
},
];
function listNames(data) {
return data
.map((item) => item.index)
.filter((val, i, arr) => arr.indexOf(val) === i);
}
function listColumns(tableName, data) {
const columnObj = data.find((item) => item.index === tableName).value;
return Object.getOwnPropertyNames(columnObj);
}
function fillRow(tableData, obj) {
const row = [];
for (let i = 0; i < tableData.columns.length; i++) {
row.push(obj[tableData.columns[i]]);
}
tableData.rows.push(row);
}
function formatJSON(data) {
const output = [];
const tables = listNames(data);
for (let i = 0; i < tables.length; i++) {
const tableData = {
title: tables[i],
columns: listColumns(tables[i], data),
rows: [],
};
for (let j = 0; j < data.length; j++) {
if (data[j].index === tableData.title) {
fillRow(tableData, data[j].value);
}
}
output.push(tableData);
}
return output;
}
console.log(formatJSON(data));
HTML
<div *ngFor="let table of tables">
<div>{{ table.title }}</div>
<table>
<thead>
<tr>
<th *ngFor="let column of table.columns">{{ column }}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of table.rows">
<td *ngFor="let value of row">{{ value }}</td>
</tr>
</tbody>
</table>
</div>
my code looks like below
export class LkBoardStatus {
id : number = 0;
descr : string = '';
}
component.ts
//...
lkBoardStatusList: LkBoardStatus[] = [];
selectedStatus: LkBoardStatus = new LkBoardStatus;
ngOnInit():void {
this.loadBoardStatusList();
../
}
loadBoardStatusList(){
this.boardService.loadBoardStatus().subscribe( posts =>{
this.data = posts;
console.log('loadBoardStatusList',this.data);
},
error => {
console.log('loadBoardStatusList - error',error);
this._errorService.handleError(error);
},
() => {
this.lkBoardStatusList = this.data.boardStatusLi;
for(let i=0; i<this.lkBoardStatusList.length; i++){
if(this.lkBoardStatusList[i].id == 70){
this.selectedStatus = this.lkBoardStatusList[i];
}
}
console.log(this.selectedStatus);
});
}
Here I have selectedStatus which is assigned a value of '70' as in for loop.
My comopnent.html is below
<th *ngSwitchCase="'statusObj'" [colSpan] = "2" style="text-align:center">
<p-multiSelect [options]="lkBoardStatusList" optionLabel="descr" [(ngModel)]="selectedStatus" defaultLabel="Select" (onChange)="dt.filter($event.value, col.field, 'in')"></p-multiSelect>
The inspect element gives me the correct object for selectedStatus but onLoad the value is not getting defaulted to 70. Where am I going wrong? Suggestions please.TIA
selectedStatus must be an array.
Try to replace
for(let i=0; i<this.lkBoardStatusList.length; i++){
if(this.lkBoardStatusList[i].id == 70){
this.selectedStatus = this.lkBoardStatusList[i];
}
}
with
this.selectedStatus = this.lkBoardStatusList.filter(item => item.id == 70);
I have a problem converting my JSON to desired format. I have something like this:
{
"one:apple": "5",
"one:orange": "10",
"two:apple": "6",
"two:orange": "11"
}
and I would like to get:
[
["one","5","10"],
["two","6","11"]
]
Any help appreciated...
Assuming that your JSON is:
{
"one:apple": "5",
"one:orange": "10",
"two:apple": "6",
"two:orange": "11"
}
and your desired JSON is:
[
["one","5","10"],
["two","6","11"]
]
Then you can iterate over the keys of your object and add the values of the elements to another hash with shorter keys, and then convert that new hash to an array of arrays.
Example
let json1 = `{
"one:apple": "5",
"one:orange": "10",
"two:apple": "6",
"two:orange": "11"
}`;
function convert(j) {
let o = JSON.parse(j);
let h = {};
for (let k of Object.keys(o)) {
let n = k.split(':')[0];
if (!h[n]) h[n] = [];
h[n].push(o[k]);
}
let a = Object.keys(h).map(k => [k].concat(h[k]));
return JSON.stringify(a);
}
let json2 = convert(json1);
console.log('json1:', json1);
console.log('json2:', json2);
See: DEMO
Example for Node 0.10
This should work on Node v0.10.x:
var json1 = '{\n' +
' "one:apple": "5",\n' +
' "one:orange": "10",\n' +
' "two:apple": "6",\n' +
' "two:orange": "11"\n' +
'}';
function convert(j) {
var o = JSON.parse(j);
var h = {};
for (var k in o) {
if (o.hasOwnProperty(k)) {
var n = k.split(':')[0];
if (!h[n]) h[n] = [];
h[n].push(o[k]);
}
}
var a = Object.keys(h).map(function (k) {
return [k].concat(h[k]);
});
return JSON.stringify(a);
}
var json2 = convert(json1);
console.log('json1:', json1);
console.log('json2:', json2);
See: DEMO
I need a stable and secure convertion algorithm (any language), that can produce the final output as "first class" JSON objects. Example:
jCard format: [["version", {}, "text", "4.0"],["fn", {}, "text", "Forrest Gump"]]
First-class JSON format: {"version":"4.0","fn":"Forrest Gump"}.
In python
first, I create one function named jcard_to_json that takes a jCard as input and converts it to a "first-class" JSON object. The function iterates over the items in the jCard, and for each item, it adds a key-value pair to the json_obj dictionary, where the key is the first item in the jCard thing and the value is the fourth item
Example:-
def jcard_to_json(jcard):
json_obj = {}
for item in jcard:
json_obj[item[0]] = item[3]
return json_obj
jcard = [["version", {}, "text", "4.0"], ["fn", {}, "text", "Forrest
Gump"]]
json_obj = jcard_to_json(jcard)
In Java.
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class JCardTest {
public static void main(String[] args) throws JSONException {
String jcardStr = "[\"vcard\","
+ " ["
+ " [\"version\", {}, \"float\", \"4.0\"],"
+ " [\"fn\", {}, \"text\", \"John Doe\"],"
+ " [\"gender\", {}, \"text\", \"M\"],"
+ " [\"categories\", {}, \"text\", \"computers\", \"cameras\"],"
+ " [\"number\", {}, \"integer\", 12345],"
+ " [\"adr\","
+ " { \"type\": \"work\" },"
+ " \"text\","
+ " ["
+ " \"\","
+ " \"Suite D2-630\","
+ " \"2875 Laurier\","
+ " \"Quebec\","
+ " \"QC\","
+ " \"G1V 2M2\","
+ " \"Canada\""
+ " ]"
+ " ]"
+ " ]"
+ " ]";
JSONArray jcard = new JSONArray(jcardStr);
jcard = jcard.getJSONArray(1);
JSONObject result = new JSONObject();
for (int i = 0; i < jcard.length(); i++) {
JSONArray arr = jcard.getJSONArray(i);
String name = arr.getString(0);
String dataType = arr.getString(2);
if (arr.length() == 4) {
switch (dataType) {
case "integer": {
long val = arr.getLong(3);
result.put(name, val);
}
break;
case "float": {
double val = arr.getDouble(3);
result.put(name, val);
}
break;
default:
Object val = arr.get(3);
if (val instanceof JSONArray) {
result.put(name, (JSONArray) val);
} else {
result.put(name, val.toString());
}
break;
}
} else {
JSONArray resArr = new JSONArray();
for (int j = 3; j < arr.length(); j++) {
resArr.put(arr.get(j).toString());
}
result.put(name, resArr);
}
}
System.out.println(result);
}
}
This ignores the 'parameter" part (arr.get(1)) of the jCard entries.
the code is verbose to not to hide important details. it could be written more compact.
Example is based on examples in the jCard RFC7095.
I found many solutions to find depth of nodes in a nested json file. but it throws me an error "maximum recursion depth exceeded "
when it set maximum recursion limit, it says "process exceeded with some error code"
As a part of my problem, I also need to find out key names of each node in the json file.
example json :
"attachments": {
"data": [
{
"media": {
"image": {
"height": 400,
"src": "https://scontent.xx.fbcdn.net/v/t1.0-1/10250217_10152130974757825_8645405213175562082_n.jpg?oh=904c1785fc974a3208f1d18ac07d59f3&oe=57CED94D",
"width": 400
}
},
"target": {
"id": "74286767824",
"url": "https://www.facebook.com/LAInternationalAirport/"
},
"title": "Los Angeles International Airport (LAX)",
"type": "map",
"url": "https://www.facebook.com/LAInternationalAirport/"
}
]
}
the output should be:
nodes:
[data [media [image[height,width,src]], target[id,url], title, type, url]]
depth: 4
If anyone else came here and found that the accepted answer does not work because Object.keys() for a string returns an array of each character of string and thus for either large objects or objects with large strings it just fails.
Here is something that works ->
function getDepth(obj){
if(!obj || obj.length===0 || typeof(obj)!=="object") return 0;
const keys = Object.keys(obj);
let depth = 0;
keys.forEach(key=>{
let tmpDepth = getDepth(obj[key]);
if(tmpDepth>depth){
depth = tmpDepth;
}
})
return depth+1;
}
Exmaple - https://jsfiddle.net/95g3ebp7/
Try the following function:
const getDepth = (
// eslint-disable-next-line #typescript-eslint/no-explicit-any
obj: Record<string, any>,
tempDepth?: number
): number => {
let depth = tempDepth ? tempDepth : 0;
if (obj !== null) {
depth++;
if (typeof obj === 'object' && !Array.isArray(obj)) {
const keys = Object.keys(obj);
if (keys.length > 0)
depth = Math.max(
...keys.map((key) => {
return getDepth(obj[key], depth);
})
);
} else if (Array.isArray(obj)) {
if (obj.length > 0)
depth = Math.max(
...obj.map((item) => {
return getDepth(item, depth);
})
);
}
}
return depth;
};
If you try this, I believe you have to get 7.
console.log(getDepth({
a: {
b: { a: [{ a: { a: [] } }] }
}
}));
function geth(obj) {
var depth = 0;
var k = Object.keys(obj);
console.log(k);
for (var i in k) {
var tmpDepth = geth(obj[k[i]]);
if (tmpDepth > depth) {
depth = tmpDepth
}
}
return 1 + depth;
}