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;
}
In this piece of code you can see a JSON request that fetches some data. I need some help to check certain opportunities of minimizing the code and getting iterations with FOR instead of many IFs. Also, it would be nice if you advise anything on the differentiation system (how to make elements differ from each other)
<script type="text/javascript">
function deleteRow0() {
$('p.row0').remove();
};
function deleteRow1() {
$('p.row1').remove();
};
function deleteRow2() {
$('p.row2').remove();
};
function deleteRow3() {
$('p.row3').remove();
};
function deleteRow4() {
$('p.row4').remove();
};
</script>
<script type="text/javascript">
function hello2() {
//GETTING JSON INFO
$.getJSON("https://rawgit.com/Varinetz/e6cbadec972e76a340c41a65fcc2a6b3/raw/90191826a3bac2ff0761040ed1d95c59f14eaf26/frontend_test_table.json", function(json) {
$('#table-cars').css("display", "grid");
for (let counter = 0; counter < json.length; counter++) {
$('#table-cars').append("<p class='row" + counter +" main-text'>" + json[counter].title + "<br/>" + "<span class='sub-text'>" + json[counter].description + "</span>" + "</p>"
+ "<p class='row" + counter +" main-text'>" + json[counter].year + "</p>"
+ "<p id='color" + [counter] + "' class='row" + counter +" main-text'>" + json[counter].color + "</p>"
+ "<p id='status" + [counter] + "' class='row" + counter +" main-text'>" + json[counter].status + "</p>"
+ "<p class='row" + counter +" main-text'>" + json[counter].price + " руб." + "</p>"
+ "<p class='row" + counter +" main-text'>" + "<button class='delete' onclick='deleteRow" + [counter] + "()'>Удалить</button>" + "</p>");
// COLOR TEXT REPLACEMENT
if ($('p#color0').text("red")){
$('p#color0').text("").append("<img src='red.png'>");
}
if ($('p#color1').text("white")) {
$('p#color1').text("").append("<img src='white.png'>");
}
if ($('p#color2').text("black")) {
$('p#color2').text("").append("<img src='black.png'>");
}
if ($('p#color3').text("green")) {
$('p#color3').text("").append("<img src='green.png'>");
}
if ($('p#color4').text("grey")) {
$('p#color4').text("").append("<img src='grey.png'>");
}
// STATUS TEXT REPLACEMENT
if ($('p#status0').text("pednding")) {
$('p#status0').text("").append("Ожидается");
}
if ($('p#status1').text("out_of_stock")) {
$('p#status1').text("").append("Нет в наличии");
}
if ($('p#status2').text("in_stock")) {
$('p#status2').text("").append("В наличии");
}
if ($('p#status3').text("out_of_stock")) {
$('p#status3').text("").append("Нет в наличии");
}
if ($('p#status4').text("in_stock")) {
$('p#status4').text("").append("В наличии");
}
}
});
}
</script>
I expect this to be something like:
1) Iteration: For each p.row(i) {
compare it to many color (json.color)};
2) Any suggestion on differentiation system (i.e. changes in the FOR section, so it gives something easier to work with, not just simple p.row(n)). Of course, if it is possible.
I'm not going to rewrite the entire script, but in principle it would be something like this:
for (i = 0; i < 5; i++) {
var colors = ["red", "white", "black", "green", "grey"];
if ($('p#color' + i).text() == colors[i]){
$('p#color' + i).text("").append("<img src='" + colors[i] + ".png'>");
}
}
#Evik Ghazarian has a quality solution for the Text Translation portion of your script. Since this is the accepted answer, he allowed me to copy his solution so that the answers would be together:
function getTranslate(input) {
var inputMap = {
"pednding": "Ожидается",
"out_of_stock": "Нет в наличии",
"in_stock": "В наличии"
}
var defaultCode = input;
return inputMap[input] || defaultCode;
}
for (let i = 0; i < 5 , i ++){
var text = $("p#status"+i).text();
$("p#status"+i).text("").append(getTranslate(text));
}
Dynamic Iteration Counters
#Barmar mentioned in the comments below that the for loops that set a max iteration via i < 5 should actually be rewritten dynamically. I'll leave it to the OP to decide the best way to do this, but a good example might be something like i < json.length as used in the OP's original for loop.
First of all your code won't work because you are setting the text rather than comparing it. Second, you don't need to compare: just set the img src to text. Like below:
REMEMBER THIS IS FOR COLOR TEXT REPLACEMENT PART OF YOUR QUESTION
for (let i = 0; i < 5 , i ++){
let color = $("p#color"+i).text() + ".png";
$("p#color"+i).text("").append("<img src=" + color + ">");
}
FOR TEXT TRANSLATION YOU CAN USE:
function getTranslate(input) {
var inputMap = {
"pednding": "Ожидается",
"out_of_stock": "Нет в наличии",
"in_stock": "В наличии"
}
var defaultCode = input;
return inputMap[input] || defaultCode;
}
for (let i = 0; i < 5 , i ++){
var text = $("p#status"+i).text();
$("p#status"+i).text("").append(getTranslate(text));
}
{
field_country: ["England", "Netherlands", "India", "Italy"],
field_continent: ["Europe"],
field_group: ["Building", "People", "Landscape"
}
I want to loop over each item and return the key and the array together with ending 'OR' for example:
field_country: "England" OR field_country: "Netherlands"
The last item should not end with 'OR' in the loop. I am not sure what the best process is for this using vanilla JS. So far my code is as follows:
Object.keys(facets).forEach(function(facetKey) {
if (facets[facetKey].length > 1) {
facetResults = facets[facetKey];
for (var i = 0; i < facetResults.length; i ++) {
if (i == 1) {
filter = "'" + facetKey + "'" + ":'" + facetResults[i] + " OR";
return filter;
} else {
filter = "'" + facetKey + "'" + ":'" + facetResults[i];
}
}
} else {
filter = "'" + facetKey + "'" + ": " + facets[facetKey] + "'";
return filter;
}
});
I would be very grateful for any assistance.
Thanks in advance.
You can do something like this with Object.entries and Array.reduce if you would like to get the final result in the form of an object:
const data = { field_country: ["England", "Netherlands", "India", "Italy"], field_continent: ["Europe"], field_group: ["Building", "People", "Landscape"] }
const result = Object.entries(data).reduce((r, [k, v]) => {
r[k] = v.join(' OR ')
return r
}, {})
console.log(result)
It is somewhat unclear what is the final format you need to result in but that should help you to get the idea. If ES6 is not an option you can convert this to:
const result = Object.entries(data).reduce(function(r, [k, v]) {
r[k] = v.join(' OR ')
return r
}, {})
So there are is no arrow function etc.
The idea is to get the arrays into the arrays of strings and use the Array.join to do the "replacement" for you via join(' OR ')
Here's the idea. In your code you are appending " or " at the end of your strings starting at index 0. I suggest you append it at the the beginning starting at index 1.
var somewords = ["ORANGE", "GREEN", "BLUE", "WHITE" ];
var retval = somewords[0];
for(var i = 1; i< somewords.length; i++)
{
retval += " or " + somewords[i];
}
console.log(retval);
//result is: ORANGE or GREEN or BLUE or WHITE
Your conditional expression if (i == 1) would only trigger on the second iteration of the loop since i will only equal 1 one time.
Try something like:
if (i < (facetResults.length - 1)) {
// only add OR if this isn't the last element of the array
filter = "'" + facetKey + "'" + ":'" + facetResults[i] + " OR";
return filter;
}
Here's your updated code:
Object.keys(facets).forEach(function(facetKey) {
if (facets[facetKey].length > 1) {
facetResults = facets[facetKey];
for (var i = 0; i < facetResults.length; i ++) {
if (i < (facetResults.length - 1)) {
filter = "'" + facetKey + "'" + ":'" + facetResults[i] + " OR";
return filter;
} else {
filter = "'" + facetKey + "'" + ":'" + facetResults[i];
}
}
} else {
filter = "'" + facetKey + "'" + ": " + facets[facetKey] + "'";
return filter;
}
});
This code displays the content of JSON file by formatting every word into sentences and then into HTML. On mouseover, words become blue. On click they become red. The next thing I want to do is to display the translation of the words (already in the json array) onclick.
https://jsfiddle.net/ve64qvtm/
var json = [
[
["Peki", "Well"],
["nedir", "what"],
["bu", "it"],
...
]
];
var arr2 = [];
for (k = 0; k < json.length; k++) {
var arr = json[k];
arr2.push('<p>');
for (i = 0; i < arr.length; i++) {
if (arr[i][0].length == 1) {
arr2.push(arr[i][0]);
} else {
arr2.push(' <span class="notclicked word ' + i + '">' + arr[i][0] + '</span>');
}
}
arr2.push('</p>');
}
document.getElementById("text").innerHTML = arr2.join('');
var words = [...document.getElementsByClassName("word")];
words.forEach(function(word) {
word.onclick = function() {
if (word.className == "clicked") {
word.className = 'notclicked';
}
if (word.className == "onmouse") {
word.className = 'clicked';
}
}
word.onmouseover = function onMouse() {
if (word.className != "clicked") {
word.className = 'onmouse';
}
}
word.onmouseout = function onMouse() {
if (word.className != "clicked") {
word.className = 'notclicked';
}
}
});
I have no idea how to do this as the text to display is a variable.
How am I supposed to do this?
How about using Twitter Bootstraps tooltip. Add jQuery, bootstraps JS and CSS; once all this is added you would need to edit the line
arr2.push(' <span class="notclicked word ' + i + '">' + arr[i][0] + '</span>');
To something like
arr2.push(' <span class="notclicked word ' + i + '" data-toggle='tooltip' data-placement='top' title='YOUR_TRANSLATION_HERE'>' + arr[i][0] + '</span>');
EDIT 2 - Updated Link:
Here is a working example
Edit 3
I Would also add a bit of margin on top and bottom so that you don´t get unexpected behaviour from the tooltips, just because there is no space.
so I'm parsing through a JSON object like so
if(val.function1!==""){
$("#contentFunction").text(val.function1);
}
if(val.function2!==""){
$("#contentFunction").text(val.function1 + "; " + val.function2);
}
if(val.function3!==""){
$("#contentFunction").text(val.function1 + "; " + val.function2
+ "; " + val.function3);
}
I'm wondiering if there is a better way of checking if my json object property is empty instead of having tons of conditions... this gets really messy if for instance I have up to val.function10
Thanks for your help
var strs = [];
for (var i = 0; i < 10; i++) {
var value = val["function" + i];
value && strs.push(value);
}
$("#contentFunction").text(strs.join("; "));
Something like this?
var content = "";
for (var prop in val) {
if (!val.hasOwnProperty(prop)) continue;
if (val[prop] !== "") {
content += "; " + val[prop];
}
}
Or in node.js (or modern browsers):
var c = Object.keys(val).filter(function (k) {
return val[k] !== "";
}).map(function (k) {
return val[k];
}).join("; ");
A tool like underscorejs will help you enumerate functions and properties.