Looping through 'layered' javascript dictionary - javascript

I'm trying to create a dictionary (I think that's the best option...) for setting up a type of address book. The idea is to return something like
contacts = {"Bruce Wayne":{"phone number":'123-456-7890', "car":"All of them"}, "Alfred":{"phone number" :'987-654-3210', "car": "limo"}, "Clark Kent":{"phone number":'951-753-8520', "car":"None, flying works."}}
This works. I can do say console.log(contacts["Bruce Wayne"]) and it returns:
{ 'phone number': '123-456-7890', car: 'All of them' }
Doing console.log(contacts["Bruce Wayne"]["phone number"]) correctly returns
123-456-7890
However, I can't figure out how to loop through each person, and then each person's phone number and car.
Using this:
for (const[key, value] of Object.entries(contacts)){
console.log(contacts[key]);
}
just returns
[object Object]
[object Object]
[object Object]
I'm trying to get (psuedo code)
[First key], [key] is [value], [key] is [value]
Bruce Wayne, phone number is 123-456-7890, car is All of them
Alfred, phone number is 987-654-3210, car is limo
Edit: I also tried
for (var person in contacts){
console.log(contacts[person])
}
which also returns [object Object]...

try this:
for (let i in contacts) {
console.log(i + ',' + JSON.stringify(contacts[i]).replace(/\":\"/g, ' is ').replace(/["{}]/g, ''));
}
or
let contacts = { "Bruce Wayne": { "phone number": '123-456-7890', "car": "All of them" }, "Alfred": { "phone number": '987-654-3210', "car": "limo" }, "Clark Kent": { "phone number": '951-753-8520', "car": "None, flying works." } }
function consoleObj(obj, separator, attrIntro) {
let result = '';
for (let name in obj) {
result += name + separator;
for (let attr in obj[name]) {
result += attr + attrIntro + obj[name][attr] + separator;
}
result += '\n';
}
return result;
}
console.log(consoleObj(contacts, ',', ' is '));
or this
function consoleObj(obj, separator, attrIntro) {
return Object.keys(obj).reduce(function(result, name) {
result += name + separator;
Object.keys(obj[name]).forEach(function(attr) {
result += attr + attrIntro + obj[name][attr] + separator;
});
result += '\n';
return result;
}, '');
}
console.log(consoleObj(contacts,',',' is '));

Your for..of loop is giving you the name and the object with the properties, so you need to change how you access the "phone number" and "car" to details['phone number'] etc...
const contacts = {"Bruce Wayne":{"phone number":'123-456-7890', "car":"All of them"}, "Alfred":{"phone number" :'987-654-3210', "car": "limo"}, "Clark Kent":{"phone number":'951-753-8520', "car":"None, flying works."}}
// join an object
const joinObject = (obj, glue = 'is', joiner = ', ') =>
Object.keys(obj)
.map(k => `${k} ${glue} ${obj[k]}`)
.join(joiner)
// fixed string
for (const [name, details] of Object.entries(contacts)) {
console.log(`${name}, phone number is ${details['phone number']}, car is ${details['car']}`);
}
// dynamic property description
for (const [name, details] of Object.entries(contacts)) {
console.log(name + ' ' + joinObject(details))
}

Related

How can I set the number of decimals on a popup using JavaScript?

I have a popup on a website that displays values from a geoJSON file. Right now, it looks as in the picture below:
This is the current code:
function popUp_cc_210303(f, l) {
var out = [];
if (f.properties) {
var url =
'<a href=/uas_tools/crop_analysis/index.php?crop=Wheat&year=2021&location=Amarillo&sublocation=Irrigation';
var parameters = '';
for (key in f.properties) {
out.push(key + ': ' + f.properties[key]);
parameters += '&' + key.replace(/\ /g, '_') + '=' + f.properties[key];
}
url +=
parameters.replace(/\ /g, '%20') + " target='_blank'>Growth analysis</a>";
out.push(url);
var url2 =
'<a href=/uas_tools/variety_analysis/index.php?crop=Wheat&year=2021&location=Amarillo&sublocation=Irrigation';
url2 += " target='_blank'>Variety analysis</a>";
out.push(url2);
l.bindPopup(out.join('<br />'));
}
}
I am trying to use out.push(key+": "+f.properties[key].toFixed(2)); but it does not work.
This is the geoJSON file structure
{ "type": "Feature", "properties": { "Row name": "row-1", "Col": "1", "plot_num": "?436", "plot_name": "?AOBS", "join_key": "?AOBS?436", "CC201014": 0.0, "CC201104": 0.0016344676538850001, "CC201120": 0.56401258728343395, "CC201217": 8.3524346613304221, "CC210113": 7.7746312091202094, "CC210224": 9.7393145428079926, "CC210303": 7.673018393542411, "CC210311": 14.576431943872961, "CC210323": 31.081778483525209, "CC210331": 30.067189249720045, "CC210408": 62.738628486108894, "CC210412": 63.94711538461538, "CC210418": 73.721694264987974, "CC210423": 70.039654826897262, "CC210430": 98.045130406889243, "CC210504": 91.969625530436502, "CC210510": 93.321666364934728, "CC210517": 85.521939491083955, "CC210525": 88.782478347768162, "CC210601": 95.859434682964093, "CC210607": 15.974798327739503, "CC210610": 0.0085470085470090006, "CC210614": 0.0, "CC210617": 0.0 }
The toFixed() method only works on floats, it appears you may have a string. You could parse it to a float first.
out.push(key + ': ' + parseFloat(f.properties[key]).toFixed(2));
One-liners are not your friend. JavaScript has its own ideas about priority. To force "order of operation", use parenthesis around any important group. Even then, code editors may format, prettify, or remove the parenthesis. Good luck finding the error then! It is best to isolate any complex operation in a named variable. A named variable is more descriptive and easier to read and reuse.
Seems like the code is calling toFixed on all values, even if the value is a string.
for (key in f.properties) {
//The first few prperties are strings, this is throwing an error as String.prototype.toFixed is not defined, so you can't call it.
out.push(key + ': ' + parseFloat(f.properties[key]).toFixed(2));
parameters += '&' + key.replace(/\ /g, '_') + '=' + f.properties[key];
}
Try this instead
for (var key in f.properties) {//include var to not add key to global scope
var val = f.properties[key]
if (typeof val === "number") {val = val.toFixed(2)}
out.push(key + ': ' + val);
parameters += '&' + key.replace(/\ /g, '_') + '=' + val;
}
Also, as noted in other answers, the values with floates might still be strings, if this is the case, you could try:
for (var key in f.properties) {//include var to not add key to global scope
var val = f.properties[key]
if (Number.isNaN(Number(val))) val = Number(val).toFixed(2)
out.push(key + ': ' + val);
parameters += '&' + key.replace(/\ /g, '_') + '=' + val;
}
You could also use the Array.reduce() method:
var out = Object.keys(f.properties)
.reduce((a, c) => (typeof f.properties[c] === `number` ?
a.push(`${c}: ${f.properties[c].toFixed(2)}`) :
a.push(`${c.replace(/\s/, `_`).toLowerCase()}: ${f.properties[c]}`), a), [])
const f = {
"type": "Feature",
"properties": {
"Row name": "row-1",
"Col": "1",
"plot_num": "?436",
"plot_name": "?AOBS",
"join_key": "?AOBS?436",
"CC201014": 0.0,
"CC201104": 0.0016344676538850001,
"CC201120": 0.56401258728343395,
"CC201217": 8.3524346613304221,
"CC210113": 7.7746312091202094,
"CC210224": 9.7393145428079926,
"CC210303": 7.673018393542411,
"CC210311": 14.576431943872961,
"CC210323": 31.081778483525209,
"CC210331": 30.067189249720045,
"CC210408": 62.738628486108894,
"CC210412": 63.94711538461538,
"CC210418": 73.721694264987974,
"CC210423": 70.039654826897262,
"CC210430": 98.045130406889243,
"CC210504": 91.969625530436502,
"CC210510": 93.321666364934728,
"CC210517": 85.521939491083955,
"CC210525": 88.782478347768162,
"CC210601": 95.859434682964093,
"CC210607": 15.974798327739503,
"CC210610": 0.0085470085470090006,
"CC210614": 0.0,
"CC210617": 0.0
}
};
const urls = [
`/uas_tools/crop_analysis/index.php`,
`/uas_tools/variety_analysis/index.php`
];
const div = document.createElement('div');
Object.keys(f.properties)
.reduce((a, c) => (typeof f.properties[c] === `number` ?
a.push(`${c}: ${f.properties[c].toFixed(2)}`) :
a.push(`${c.replace(/\s/, `_`).toLowerCase()}: ${f.properties[c]}`), a), [])
.map(el => {
const br = document.createElement('br');
const span = document.createElement('span');
span.innerText = el;
div.appendChild(span).appendChild(br);
});
const parameters = {
crop: `Wheat`,
year: 2021,
location: `Amarillo`,
sublocation: `Irrigation`
};
urls.map(pathName => {
const url = new URL(pathName, window.location.origin);
Object.keys(parameters)
.map(elem => url.searchParams.set(elem, parameters[elem]));
const br = document.createElement('br');
const a = document.createElement('a');
a.href = url.toString();
a.target = `_blank`;
a.innerText = url.pathname
.split(`/`)
.slice(-2, -1)[0]
.split(`_`)
.map(el => el.charAt(0).toUpperCase() + el.substr(1).toLowerCase())
.join(` `);
div.appendChild(a).appendChild(br);
});
document.querySelector(`body`).appendChild(div);
body {
font-family: sans-serif;
font-size: 12px;
}

how can I display the child object values(children) with the main object?

let state=["Karnataka","Andharapradesh","Tamilnadu"];
console.log(state);
document.write("<br>"+state);
for (i=0;i<=state.length-1;i++){
document.write(state[i]);
}
// -- question part--
let person = {
name: prompt("Your name"),
age: prompt("age?"),
spouse:prompt("spouse name"),
address: prompt("enter your address"),
children :{
child1: "kamal",
child2: prompt("name of the child"),
}
}
console.log(person);
for(let values in person){
document.write( "<br>" + values + ":" + person[values]);
}
// output
Karnataka,Andharapradesh,Tamilnadu
name:jashdk
age:khsald
spouse:kashdl
address:lksadlka
children:[object Object]
// it will display only main object values and the child value as children:[object Object]
You could make a tree search like this:
const treeSearch = (obj) => {
for(let values in obj) {
document.write("<br>" + values + ":" + obj[values]);
}
}
Then you can implement that by checking if the person object contains a object as one of it's keys values.
// Full Code
let person = {
name: prompt("Your name"),
age: prompt("age?"),
spouse:prompt("spouse name"),
address: prompt("enter your address"),
children :{
child1: "kamal",
child2: prompt("name of the child"),
}
}
const treeSearch = (obj) => {
for(let values in obj) {
document.write("<br>" + values + ":" + obj[values]);
}
}
for(let values in person){
if(typeof person[values] == "object") {
treeSearch(person[values]);
} else {
document.write( "<br>" + values + ":" + person[values]);
}
}
That should work.

RegEx repeating capture

Can a RegEx be created to pull the same pattern over and over?
https://regex101.com/r/BEOHLh/2/
Tried something like this too:
.* [(.*?)='(.*?)']{0-5}.*
to allow for everything in square brackets to repeat.
I could parse it without RegEx, but seem like RegEx would be the best. I'm converting some WordPress shortcodes to a NodeJS based system. Need to extract id and label (either one may be optional), so here are three test cases and the code I tried.
var testArray = ["[include_word id='110']", "[include_word label='bah']", "[include_word id='987' label='bah-beh']"];
testArray.forEach (processArrayItemRegex);
function processArrayItemRegex (item, index) {
console.log ("index:" + index + " item:" + item);
//var regexPattern = /.*id='(<id>.*?)'.*label='(<label>.*?)'.*/g;
//var regexPattern = /.*(?:id='(.*?)').*(?:label='(.*?)').*/g;
var regexPattern = /.* (.*?)='(.*?)'.*/g;
//const { groups: segments } = item.match(regexPattern);
var segments = item.match(regexPattern);
console.dir(segments, { depth: null });
//console.log(segments);
id = segments[0];
label = segments[1];
console.log("id=" + id + " label=" + label);
}
Current output:
index:0 item:[include_word id='110']
[ '[include_word id=\'110\']' ]
id=[include_word id='110'] label=undefined
index:1 item:[include_word label='bah']
[ '[include_word label=\'bah\']' ]
id=[include_word label='bah'] label=undefined
index:2 item:[include_word id='987' label='abc']
[ '[include_word id=\'987\' label=\'abc\']' ]
id=[include_word id='987' label='abc'] label=undefined
The code below works to convert it to JSON and let's me access the variables, it's just not as elegant as I would like:
function processArrayItem (item, index) {
console.log ("index:" + index + " item:" + item);
//remove unneeded wrappers
item = item.replace("include_word ","")
item = item.replace("[","{").replace("]","}");;
item = item.replace(/'/g, '"');
item = item.replace('id=','"id":');
item = item.replace('label=','"label":');
item = item.replace('transliteration=','"transliteration"');
var itemObj = JSON.parse(item);
console.log("id=" + itemObj.id + " label=" + itemObj.label);
}
If I understand correctly, the thing you're missing is the matchAll function.
Try this as a starting point:
for (let match of "[include_word id='987' label='bah-beh']".matchAll(
/(\S+)='(\S+)'/g
)) {
console.log({ key: match[1], value: match[2] });
}
I am not sure whether this is what you want,
testArray.forEach (processArrayItemRegex);
function processArrayItemRegex (item, index) {
console.log ("index:" + index + " item:" + item);
var regexPattern = /(\S+)='(\S+)'/g;
//console.log(regexPattern);
var id = label = 'undefined';
var segments ;
while ((segments = regexPattern.exec(item)) != null)
{
//console.dir(segments , { depth: null });
var key = segments [1];
var value = segments [2];
if (key == 'id')
id = value;
if (key == 'label')
label = value;
};
console.log("id=" + id + " label=" + label);
}
Console output:
index:0 item:[include_word id='110']
id=110 label=undefined
index:1 item:[include_word label='bah']
id=undefined label=bah
index:2 item:[include_word id='987' label='bah-beh']
id=987 label=bah-beh

Read a jsonObject recursively in javascript [duplicate]

This question already has answers here:
Check if a value is an object in JavaScript
(54 answers)
Closed 4 years ago.
I am trying to read a nested object as a key-value. I am using a recursive function to be able to enter each object and print its key-value. The problem is when I try to read a key-value where the value is null. It simply prints null, as if the key did not exist.
function readJsonObject(jsonObject){
for(var key in jsonObject){
if (typeof jsonObject[key] === 'object') {
readJsonObject(jsonObject[key]);
} else{
console.log(key + ": " + jsonObject[key]);
}
}
};
var text = '{ "employees" : [' +
'{ "firstName":"John" , "lastName":null },' +
'{ "firstName":"Anna" , "lastName":"Smith" },' +
'{ "firstName":"Peter" , "lastName":"Jones" } ]}';
var obj = JSON.parse(text);
readJsonObject(obj);
I should print:
firstName: John
lastName: null
firstName: Anna
lastName: Smith
firstName: Peter
lastName: Jones
But prints:
firstName: John
firstName: Anna
lastName: Smith
firstName: Peter
lastName: Jones
(Note that John's last name is not printed)
Any idea?
Here is a sample of a function that prints all the key : value for all objects recursively
function readJsonObject(jsonObject) {
if (Array.isArray(jsonObject)) {
for (var el of jsonObject) {
readJsonObject(el)
}
return
}
else if (typeof jsonObject === 'object' && jsonObject.constructor === Object) {
for (var key of Object.keys(jsonObject)) {
var value = jsonObject[key];
var toDisplay;
if (value && typeof value === 'object' && value.constructor === Object) {
toDisplay = readJsonObject(value);
} else if (Array.isArray(value)) {
toDisplay = JSON.stringify(value);
readJsonObject(value);
} else {
toDisplay = value;
}
console.log(key + ": " + toDisplay);
}
}
return jsonObject;
}
var text = '{ "employees" : [' +
'{ "firstName":"John" , "lastName":null },' +
'{ "firstName":"Anna" , "lastName":"Smith" },' +
'{ "firstName":"Peter" , "lastName":"Jones" } ]}';
var obj = JSON.parse(text);
console.log(readJsonObject(obj))
I handled Arrays as a special type, but please note there are other types that you maybe should check, since object encapsulate many other types, for instance null.

jQuery Nested Array..get immediate parent id

I have the following array
array = [
{
"id": "67",
"sub": [
{
"id": "663",
},
{
"id": "435",
}
]
},
{
"id": "546",
"sub": [
{
"id": "23",
"sub": [
{
"id": "4",
}
]
},
{
"id": "71"
}
]
}
]
I am currently looping throught the array as follows
calling the array:
processArray(array);
the function loop
function processArray(arr)
{
for(var item in arr) {
var value = arr[item];
var order = item;
var itemID = value.id;
if(itemID != null)
{
$('.text').append(" ORDER : " + order + " Item ID : " + itemID + "<br />" );
}
if(typeof(value) == 'object') { //If it is an array,
processArray(arr[item]);
}
}
}
Currently i am getting the order of the item and the current ID no problem. What i need however (for my database schema) is for each item get the ID of its parent if there is one.
Do i need to pass the parent to each node? Or is there an easier way?
Thanks
Working demo
Include an optional parameter parentID in the function; by doing this, you can still use the processArray(array); syntax to process the original array.
function processArray(arr, parentID)
{
for(var item in arr) {
var value = arr[item];
var order = item;
var itemID = value.id;
if(itemID != null)
{
var output = " ORDER : " + order + " Item ID : " + itemID;
if( parentID ) { output += " PARENT : " + parentID; }
$('.text').append( output + "<br />");
}
// PROCESS SUB-ARRAY
if( typeof(value.sub) == 'object') { //If it is an array,
processArray( value.sub, itemID );
}
}
}
Use an auxiliary function that has id as part of its signature:
function processArray(arr) {
function _processArray(arr, id) {
for (var item in arr) {
var value = arr[item];
var order = item;
var itemID = value.id; // you need to change this because on the second call you pass in a string and not just an object
var parentId = id;
// Commenting the if statement that you had here actually shows the parent id's now.
$('.text').append(" ORDER : " + order + " Item ID : " + itemID + " Parent Id : " + parentId + "<br />");
if (typeof value === "object") { //use instanceof,
_processArray(arr[item], itemID);
}
}
}
_processArray(arr, 0);
}

Categories

Resources