I have an obj like this:
var obj = { thing1 : { name: 'test', value: 'testvalue1'},
thing2 : { name: 'something', thing4: {name:'test', value: 'testvalue2'}},
}
I want to write a function like findByName(obj, 'test').It returns all the matching sub-objects with the same name. So it should return:
{ name: 'test', value: 'testvalue1'}
{name:'test', value: 'testvalue2'}
Right now this is what i have:
function findByName(obj, name) {
if( obj.name === name ){
return obj;
}
var result, p;
for (p in obj) {
if( obj.hasOwnProperty(p) && typeof obj[p] === 'object' ) {
result = findByName(obj[p], name);
if(result){
return result;
}
}
}
return result;
}
obviously it only return the first matching.. how to improve this method?
You need to push the results into an array and make the function return an array.
Also, do a sanity check whether the object is null or undefined to avoid errors.
Here is your code modified.
Note: I have also modified the parent object ,which is "obj", by adding a "name" property with value "test" so the result should have the parent object in the result as well.
function findByName(obj, name) {
var result=[], p;
if(obj == null || obj == undefined)
return result;
if( obj.name === name ){
result.push(obj);
}
for (p in obj) {
if( obj.hasOwnProperty(p) && typeof obj[p] === 'object') {
newresult = findByName(obj[p], name);
if(newresult.length>0){
//concatenate the result with previous results found;
result=result.concat(newresult);
}
}
}
return result;
}
var obj = { thing1 : { name: 'test', value: 'testvalue1'},
thing2 : { name: 'something', thing4: {name:'test', value: 'testvalue2'}},
name:'test' //new property added
}
//execute
findByName(obj,"test");
Run this in your console and upvote if this helps you.
Related
I am looking for a way to parse the JSON received from Telnyx (through a webhook) in a Google spreadsheet, using in cell formulas.
The ideal outcome is to having data from each line in a separate column (including lines with no data), with the correct heading.
JS would also be an option.
I've tried using FILTER and REGEXMATCH with some results, but I couldn't manage to get a decent output.
Any help would be greatly appreciated.
The data received from Telnyx looks like this:
"{
""data"": {
""event_type"": ""message.received"",
""id"": ""0d9c22"",
""occurred_at"": ""2022-07-23T04:52:08.642+00:00"",
""payload"": {
""cc"": [],
""completed_at"": null,
""cost"": null,
""direction"": ""inbound"",
""encoding"": ""GSM-7"",
""errors"": [],
""from"": {
""carrier"": """",
""line_type"": """",
""phone_number"": ""+447""
},
""id"": ""eb17"",
""media"": [],
""messaging_profile_id"": ""4001"",
""organization_id"": ""8ab"",
""parts"": 2,
""received_at"": ""2022-07-20T04:52:08.464+00:00"",
""record_type"": ""message"",
""sent_at"": null,
""subject"": """",
""tags"": [],
""text"": ""SMS goes here.\nThanks"",
""to"": [
{
""carrier"": ""Telnyx"",
""line_type"": ""Wireless"",
""phone_number"": ""+447"",
""status"": ""webhook_delivered""
}
],
""type"": ""SMS"",
""valid_until"": null,
""webhook_failover_url"": null,
""webhook_url"": ""https://script.google.com/XXXXX""
},
""record_type"": ""event""
},
""meta"": {
""attempt"": 1,
""delivered_to"": ""https://script.google.com/""
}
}"
Try this recursive function (A1 contains the data)
=functionTelnyx(A1)
put in your script editor
function functionTelnyx(json) {
const telnyx = JSON.parse(json.slice(1,-1).replace(/""/g,'"').replace(/(\r\n|\n|\r|\t| )/gm, ""))
let result = [];
recursion(telnyx)
function recursion(obj, path) {
if (path == undefined) { path = 'telnyx' }
const regex = new RegExp('[^0-9]+');
for (let p in obj) {
let newPath = (regex.test(p)) ? path + '.' + p : path + '[' + p + ']';
if (obj[p] != null) {
if (typeof obj[p] != 'object' && typeof obj[p] != 'function') {
result.push([ p, obj[p]]);
}
if (typeof obj[p] == 'object') {
recursion(obj[p], newPath);
}
}
}
}
return result
}
edit:
for multiple json, and horizontal layout
for headers, put in B1 =telnyx_headers(A2)
for contents, put in B2 and drag below =telnyx_contents(A2), then =telnyx_contents(A3) and so forth
with
function telnyx_headers(json) {
const telnyx = JSON.parse(json.slice(1, -1).replace(/""/g, '"').replace(/(\r\n|\n|\r|\t| )/gm, ""))
let result = [];
recursion(telnyx)
function recursion(obj, path) {
if (path == undefined) { path = 'telnyx' }
const regex = new RegExp('[^0-9]+');
for (let p in obj) {
let newPath = (regex.test(p)) ? path + '.' + p : path + '[' + p + ']';
if (obj[p] != null) {
if (typeof obj[p] != 'object' && typeof obj[p] != 'function') {
result.push(p);
}
if (typeof obj[p] == 'object') {
recursion(obj[p], newPath);
}
}
}
}
return [result]
}
function telnyx_contents(json) {
const telnyx = JSON.parse(json.slice(1, -1).replace(/""/g, '"').replace(/(\r\n|\n|\r|\t| )/gm, ""))
let result = [];
recursion(telnyx)
function recursion(obj, path) {
if (path == undefined) { path = 'telnyx' }
const regex = new RegExp('[^0-9]+');
for (let p in obj) {
let newPath = (regex.test(p)) ? path + '.' + p : path + '[' + p + ']';
if (obj[p] != null) {
if (typeof obj[p] != 'object' && typeof obj[p] != 'function') {
result.push(obj[p]);
}
if (typeof obj[p] == 'object') {
recursion(obj[p], newPath);
}
}
}
}
return [result]
}
optional: array-enabled version
To process a larger number of JSON strings in the column A2:A, use this formula:
=telnyx(A2:A)
The telnyx() function uses telnyx_headers() and telnyx_contents() while observing custom function best practices.
/**
* Converts one or more JSON strings to a horizontal table.
* Row 1 will show field names and subsequent rows the
* data in each field.
*
* #param {A2:A} json The JSON strings to convert to a table.
* #customfunction
*/
function telnyx(json) {
'use strict';
if (!Array.isArray(json)) {
json = [json];
}
json = json.flat();
let result = telnyx_headers(json[0]);
json.forEach(j => result = result.concat(j ? telnyx_contents(j) : [null]));
return result;
}
I want to serialize circular reference to JSON
This is the part generating the JSON array and it causes a circular reference because it creates a child inside an element and I want to display the result.
const mappedData = this.state.treeData
.filter(data => data.title === "Category")
.map(categ => {
const { id, title, children, subtitle, type } = categ;
function getChildren(children) {
return children
? children.map(child => {
if (child.title === "Item" || child.title === "Group") {
const data = {
id: child.id,
name: child.subtitle,
type: child.type,
children: getChildren(child.children),
child_id: child.children
? child.children.map(child => child.id)
: []
};
if (data.children.length === 0) delete data.children;
if (data.child_id.length === 0) delete data.child_id;
return data;
} else {
return {
id: child.id,
name: child.subtitle,
type: child.type
};
}
})
: [];
}
const data = {
id: id,
name: subtitle,
type: type,
children: getChildren(children),
child_id: children ? children.map(child => child.id) : []
};
if (data.children.length === 0) delete data.children;
if (data.child_id.length === 0) delete data.child_id;
return data;
});
The HTML part that calls the stringify method
<div className="json">
<p> {JSON.stringify(mappedData)}</p>
</div>
I found a Replacer that it works but the JSON result is too long for what I need
const getCircularReplacer = () => {
const seen = new WeakSet();
return (key, value) => {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) {
return;
}
seen.add(value);
}
return value;
};
};
JSON.stringify(circularReference, getCircularReplacer());
And here's the result :
[{"id":"7a69fc68","name":{"type":"input","key":null,"ref":null,"props":{"className":"inputSubs","type":"text","placeholder":"Category Name"},"_owner":{"tag":1,"key":null,"stateNode":{"props":{},"context":{},"refs":{},"updater":{},"notificationAlert":{"current":{"props":{},"refs":{"notifications":{"__reactInternalInstance$6qqi1p3qi9b":{"tag":5,"key":null,"elementType":"div","type":"div","return":{"tag":1,"key":null,"return":{"tag":5,"key":null,"elementType":"div","type" .. etc
Any Help ?
I am getting a warning on the following function
function currencySubmenuTitle(ctx) {
let id = Object.keys(currencies).find(element => {
if (currencies[element].id === ctx.match[1]) {
return element
}
})
if (typeof id === 'undefined' || id === null) {
return "No match found"
} else {
return `💰 ${toTitleCase(id)} : ${currencies[id].current}`
}
}
Note: My id and element are different, so I can't just take the element and use that as the string return.
The warning is:
2:51 warning Expected to return a value at the end of arrow function array-callback-return
2:51 warning Expected to return a value at the end of arrow function consistent-return
How do I return my value in this function in a compliant way (aka not how I am doing it)
Can I thenify this? Run the if statement based on the return of the array-evaluation?
The evaluation of the statement can happen in the return line, so no specific if-statement is needed here. Simply do:
function currencySubmenuTitle(ctx) {
let id = Object.keys(currencies).find(element => {
return currencies[element].id === ctx.match[1]
})
if (typeof id === 'undefined' || id === null) {
return "No match found"
} else {
return `💰 ${toTitleCase(id)} : ${currencies[id].current}`
}
}
Below is my custom pipe where I am unable to access the members of the customfilter array which is of type Item.
import { Pipe, PipeTransform } from '#angular/core';
import {Bus} from '/home/pavan/Desktop/Pavan/apstrtcAngular/src/app/Bus';
import { Item } from './Item';
#Pipe({
name: 'busFilter'
})
export class BusFilterPipe implements PipeTransform {
transform(items: Bus[], customfilter: Item): Bus[] {
if(!items || !customfilter)
{
return items;
}
return items.filter((item: Bus)=>
this.applyFilter(item, customfilter));
}
applyFilter(bus:Bus, customfilter: Item):
boolean{
if( customfilter[0].item_id){
if(typeof customfilter[0].item_id==='string'){
if(typeof bus.bustype==='string')
{
if(customfilter[0].item_id===bus.bustype)
{
return false;
}
} }
}
return true;
}
}
Below is my Item.ts and ng multiselect.
export class Item {
/**
* #type {number} id Unique numeric identifier.
*/
item_id: string;
item_text:string;
}
<ng-multiselect-dropdown class="ngfilter"
[placeholder]="'Select BusType'"
[data]="BusTypes"
[(ngModel)]="customfilter"
[settings]="dropdownSettings"
(onSelect)="onItemSelect($event)"
(onSelectAll)="onSelectAll($event)"></ng-multiselect-dropdown>
I am unable to find the issue here, I cannot look at the value of item_id during debugging too. please help me to know where the issue is. Thank you.
import { Pipe, PipeTransform } from '#angular/core';
import {Bus} from '/home/pavan/Desktop/Pavan/apstrtcAngular/src/app/Bus';
import { Item } from './Item';
import { forEach } from '#angular/router/src/utils/collection';
#Pipe({
name: 'busFilter'
})
export class BusFilterPipe implements PipeTransform
{
transform(items: Bus[], customfilter: Item[]): Bus[] {
let ResultSet: Bus[] = [];
if (!items || !customfilter) {
return items;
}
else if (customfilter.length == 0) {
return items;
}
else{
for (let i = 0; i < items.length; i++) {
for (let j = 0; j < customfilter.length; j++) {
if (customfilter[j].item_text === items[i].bustype) {
ResultSet.push(items[i]);
console.log("Result Set =" + ResultSet);
}
}
}
return ResultSet;
}
}
}
Based on your comments and my understanding of your code written in the pipe, modify your pipe like this (please read through the comments in the code):
transform(items: Bus[], customfilter: Item[]): Bus[] {
if(!items || !customfilter)
{
return items;
}
// making custom filter an Array if it isn't already
customFilter = customFilter instanceof Array ? customFilter : [customFilter];
// you seem to ignore the custom filters which don't have item_id
customFilter = customFilter.filter((eachCustom) => eachCustom.item_id);
// create an array of new items which satisfy your criteria
return items.reduce((acc, eachBus) => {
// if bus's bustype is not string then no need to filter
if (typeof eachBus.bustype != 'string') {
acc.push(eachBus)
}
else {
// if the bustype is a string
// then you have to see if this bus's bustype matches any of the custom filters and it's id type
// if not found then that bus should be present in the final bus list
let filterFound = customFilter.findIndex((eachFilter) => {
return (typeof eachFilter.item_id === 'string') && (typeof eachBus.bustype === 'string') && (eachFilter.item_id === eachBus.bustype);
});
if (filterFound === -1) {
// this bus is not found in the filter
acc.push(eachBus)
}
}
return acc;
}, [])
}
Below is a script in javascript to verify the result
function transform(items, customfilter) {
if(!items || !customfilter)
{
return items;
}
// making custom filter an Array if it isn't already
customFilter = customFilter instanceof Array ? customFilter : [customFilter];
// you seem to ignore the custom filters which don't have item_id
customFilter = customFilter.filter((eachCustom) => eachCustom.item_id);
// create an array of new items which satisfy your criteria
return items.reduce((acc, eachBus) => {
// if bus's bustype is not string then no need to filter
if (typeof eachBus.bustype != 'string') {
acc.push(eachBus)
}
else {
// if the bustype is a string
// then you have to see if this bus's bustype matches any of the custom filters and it's id type
// if not found then that bus should be present in the final bus list
let filterFound = customFilter.findIndex((eachFilter) => {
return (typeof eachFilter.item_id === 'string') && (typeof eachBus.bustype === 'string') && (eachFilter.item_id === eachBus.bustype);
});
if (filterFound === -1) {
// this bus is not found in the filter
acc.push(eachBus)
}
}
return acc;
}, [])
}
let buses = [{bustype: 1}, {bustype: "volvo-ac"}, {bustype: "volvo-non-ac"}, {bustype: "non-volvo-ac"}, {bustype: "non-volvo-non-ac"}]
let customFilter = [{item_id: "volvo-ac"}, {item_id: "non-volvo-ac"}]
console.log(transform(buses, customFilter))
// expected output won't contain the buses present in the filter
Routes:
exports.authenticate = function(req, res) {
//connection.connect();
var sql="SELECT * from users where username='"+req.body.user+"' and password='"+req.body.pass+"' LIMIT 1";
connection.query(sql, function(err, rows, fields) {
if (err) throw err;
//res.send('Your data is: ', rows);
var str="Hi, <b>"+rows[0].name+"</b> ("+rows[0].email+")";
sql="SELECT username,name from users ORDER BY name";
connection.query(sql, function(err, datarows, fields) {
if (err) throw err;
//res.send('Your data is: ', rows+' <br/> All Users are : ', datarows.length+"<a href='/'>Login</a>");
console.log(datarows);
res.render('home.jade', {title: 'Home',result:datarows});
/*---------------The above calling jade-----------------*/
});
//str+="<a href='/'>Login</a>";
//res.send(str);
//connection.end();
});
Jade:
extends layout
block content
//p #{result}
-var list=[ { username: 'rohankumar1524', name: 'Rohan Kumar' }]
each item, i in result
p test #{item.length}
each role,j in item
p #{role}
Output
test
rohankumar1524
Rohan Kumar
unusual output
function (parser, fieldPackets, typeCast, nestTables, connection) { var self = this; var next = function () { return self._typeCast(fieldPacket, parser, connection.config.timezone, connection.config.supportBigNumbers); }; for (var i = 0; i < fieldPackets.length; i++) { var fieldPacket = fieldPackets[i]; var value; if (typeof typeCast == "function") { value = typeCast.apply(connection, [ new Field({ packet: fieldPacket, parser: parser }), next ]); } else { value = (typeCast) ? this._typeCast(fieldPacket, parser, connection.config.timezone, connection.config.supportBigNumbers) : ( (fieldPacket.charsetNr === Charsets.BINARY) ? parser.parseLengthCodedBuffer() : parser.parseLengthCodedString() ); } if (typeof nestTables == "string" && nestTables.length) { this[fieldPacket.table + nestTables + fieldPacket.name] = value; } else if (nestTables) { this[fieldPacket.table] = this[fieldPacket.table] || {}; this[fieldPacket.table][fieldPacket.name] = value; } else { this[fieldPacket.name] = value; } } }
Also this
function (field, parser, timeZone, supportBigNumbers) { switch (field.type) { case Types.TIMESTAMP: case Types.DATE: case Types.DATETIME: case Types.NEWDATE: var dateString = parser.parseLengthCodedString(); if (dateString === null) { return null; } if (timeZone != 'local') { if (field.type === Types.DATE) { dateString += ' 00:00:00 ' + timeZone; } else { dateString += timeZone; } } return new Date(dateString); case Types.TINY: case Types.SHORT: case Types.LONG: case Types.INT24: case Types.YEAR: case Types.FLOAT: case Types.DOUBLE: case Types.LONGLONG: case Types.NEWDECIMAL: var numberString = parser.parseLengthCodedString(); return (numberString === null || (field.zeroFill && numberString[0] == "0")) ? numberString : ((supportBigNumbers && Number(numberString) > IEEE_754_BINARY_64_PRECISION) ? numberString : Number(numberString)); case Types.BIT: return parser.parseLengthCodedBuffer(); case Types.STRING: case Types.VAR_STRING: case Types.TINY_BLOB: case Types.MEDIUM_BLOB: case Types.LONG_BLOB: case Types.BLOB: return (field.charsetNr === Charsets.BINARY) ? parser.parseLengthCodedBuffer() : parser.parseLengthCodedString(); case Types.GEOMETRY: return parser.parseGeometryValue(); default: return parser.parseLengthCodedString(); } }
But when I use this code in jade
extends layout
block content
//p #{result}
-var list=[ { username: 'rohankumar1524', name: 'Rohan Kumar' }]
each item, i in list // list in place of result
p test #{item.length}
each role,j in item
p #{role}
The output is
test
rohankumar1524
Rohan Kumar
which is expected
Why this problem occurs, is any other module I have install by npm
Try this:
Jade
extends layout
block content
each item in result
p item.username
p item.name