Why Array.length does not work with key string - arrays

I've been reading and doing some testing and found that the command. "Length" of the Array () javascript does not work when the array's keys are string. I found that to run this command, I use whole keys.
However, I wonder if any of you can tell me why this rule? Limitation of language, or specification of logic? Or, my mistake ...?
Thanks
Obs.: The key to my array is a string (name of component) but the values and the array of objects.
Declarating:
objElementos = new Array();
Object:
objElemento = function(Id){
this.Id = $('#' + Id).attr('id');
this.Name = $('#' + Id).attr('name');
this.CssLeft = $('#' + Id).css('left');
this.CssBottom = $('#' + Id).css('bottom');
this.Parent = $('#' + Id).parent().get(0);
this.Childs = new Array();
this.addChild = function(IdObjChild) {
try {
if ($('#' + IdObjChild).attr('id') != '')
this.Childs[$('#' + IdObjChild).attr('id')] = new objElemento(IdObjChild);
} catch(err){ $('#divErrors').prepend('<p>' + err + '</p>'); }
}
this.getChildCount = function(){
try {
$('#divErrors').prepend('<p>' + this.Childs.length + '</p>');
return this.Childs.length;
} catch(err){ $('#divErrors').prepend('<p>' + err + '</p>'); }
}
this.updateCssLeft = function(CssPosition){
try {
$('#divErrors').prepend('<p>updateCssLeft:' + this.Id + ' / ' + this.CssLeft + '</p>');
$('#' + this.Id).css('left',CssPosition);
this.CssLeft = $('#' + this.Id).css('left');
$('#divErrors').prepend('<p>updateCssLeft:' + this.Id + ' / ' + this.CssLeft + '</p>');
} catch(err){ $('#divErrors').prepend('<p>' + err + '</p>'); }
}
this.updateCssBottom = function(CssPosition){
try {
$('#divErrors').prepend('<p>updateCssBottom:' + this.Id + ' / ' + this.CssBottom + '</p>');
$('#' + this.Id).css('bottom',CssPosition);
this.CssBottom = $('#' + this.Id).css('bottom');
$('#divErrors').prepend('<p>updateCssBottom:' + this.Id + ' / ' + this.CssBottom + '</p>');
} catch(err){ $('#divErrors').prepend('<p>' + err + '</p>'); }
}
}
Use:
objElementos['snaptarget'] = new objElemento('snaptarget');

When using string "indexers" on an array, you are effectively treating it as an object and not an array, and tacking extra fields onto that object. If you are not using integer indexes, then there's not really much point in using the Array type and it makes more sense to use the Object type instead. You could count the fields as follows:
var c=0;
for(var fieldName in obj)
{
c++;
}

Since arrays with key strings are objects, you can use:
Object.keys(objElementos).length

The reason is because when you are using strings as your keys, you're really declaring an object, rather than an array. As such, there is no length attribute for objects.

Related

My table rows shows the same data on each row - data feched from xml files

I'm new at Javascript and i'm trying to pull data from multiple XML files.
Right now i have maneged to create 5 rows of data, but all rows contains data from the same file.
I have been trying to store the data in another array than the original one, but nothing i have tried seems too work.
My HTML code is here:
<body>
<button type="button" onclick="loadXMLDoc()" >Get my PTO DATA</button>
<br><br>
<table id="demo">
<tbody>
</tbody>
</table>
</body>
And Javascript here:
var arr = ["018338464.xml", "018340087.xml", "018340096.xml", "018340106.xml", "018340153.xml"],
cnt = 0, xmlhttp = new XMLHttpRequest(), method = "GET";
function loadXMLDoc() {
xmlhttp.open(method, arr[cnt], true);
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState === XMLHttpRequest.DONE && xmlhttp.status == 200) {
buildTable();
}
};
xmlhttp.send();
}
function buildTable(file, xmlDoc) {
var i;
var a = 1
var xmlDoc = xmlhttp.responseXML;
var table = "<tr><th>DisplayNumber</th><th>Client</th><th>IpgClientId</th><th>Location</th><th>PracticeArea</th><th>Type</th><th>Application number</th><th>Application date</th><th>Classes</th><th>Class description</th><th>Status</th></tr>";
var x = xmlDoc.getElementsByTagName("Transaction");
for (var d = 0; d < arr.length; d++) {
console.log(`${d}: ${arr[d]}`);
var store = new Array();
for (i = 0; i < x.length; i++) {
table += "<tr><td>" +
x[i].getElementsByTagName("TransactionIdentifier")[0].childNodes[0].nodeValue +
"</td><td>" +
x[i].getElementsByTagName("MarkVerbalElementText")[0].childNodes[0].nodeValue +
"</td><td>" +
x[i].getElementsByTagName("Identifier")[0].childNodes[0].nodeValue +
"</td><td>" +
x[i].getElementsByTagName("RegistrationOfficeCode")[0].childNodes[0].nodeValue +
"</td><td>" +
x[i].getElementsByTagName("TransactionCode")[0].childNodes[0].nodeValue +
"</td><td>" +
x[i].getElementsByTagName("MarkFeature")[0].childNodes[0].nodeValue +
"</td><td>" +
x[i].getElementsByTagName("ApplicationNumber")[0].childNodes[0].nodeValue +
"</td><td>" +
x[i].getElementsByTagName("ApplicationDate")[0].childNodes[0].nodeValue +
"</td><td>" +
x[i].getElementsByTagName("ClassNumber")[0].childNodes[0].nodeValue +
"</td><td>" +
x[i].getElementsByTagName("GoodsServicesDescription")[2].childNodes[0].nodeValue +
"</td><td>" +
x[i].getElementsByTagName("MarkCurrentStatusCode")[0].childNodes[0].nodeValue +
"</td></tr>";
}
store[d] = a;
a++;
}
document.getElementById("demo").innerHTML = table;
}
All feedback are welcome, i'm also open to redo it all if someone has a better solution to my problem
You always load arr[cnt], and cnt is always zero. If you want loadXMLDoc to load the entire set, then you want
function loadXMLDoc() {
arr.forEach( function(name) {
xmlhttp.open(method, name, true);
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState === XMLHttpRequest.DONE && xmlhttp.status == 200) {
buildTable();
}
};
xmlhttp.send();
});
}

How to map and send emails to specific users who don't meet a certain criteria in Javascript/GAS

I am new in Javascript and bit by bit I have used resources here on StackOverflow to build on a project that uses external API to get time entries for users from the 10k ft project management system. I have finally have different functions together as follows:
Calls for user details which includes user_id
Get the time entries and sums up for every user who's approval has a value (pending or approval) in a specific date range. Those without approval will be ignored in the summation and their total entries left at 0.
My challenge now is to have only those with 0 as total hours of time entries receive emails to update their time entries. This code doesn't seem to select only those with 0 and send emails specifically to them. I will appreciate any pointers and/or assistance. after sending the email, this should be recorded on Google sheet
var TKF_URL = 'https://api.10000ft.com/api/v1/';
var TKF_AUTH = 'auth'
var TKF_PGSZ = 2500
var from = '2020-01-20'
var to = '2020-01-26'
var options = {
method: 'get',
headers: {
Authorization: 'Bearer ' + TKF_AUTH
}
};
function getUsers() {
var userarray = [];
var lastpage = false;
var page = 1;
do {
// gets 10kft data
var users = read10k_users(page);
// writes data from current page to array
for (var i in users.data) {
var rec = {};
// pushing of mandatory data
rec.id = users.data[i].id;
rec.display_name = users.data[i].display_name;
rec.email = users.data[i].email;
userarray.push(rec);
}
// checks if this is the last page (indicated by paging next page link beeing null
if (users.paging.next != null) {
lastpage = false;
var page = page + 1;
} else {
lastpage = true;
}
}
while (lastpage == false);
return (userarray);
return (userarray);
}
function read10k_users(page) {
var endpoint = 'users?';
var url = TKF_URL + endpoint + 'per_page=' + TKF_PGSZ + '&auth=' + TKF_AUTH + '&page=' + page;
var response = UrlFetchApp.fetch(url, options);
var json = JSON.parse(response);
//Logger.log(json.data)
return (json);
}
function showTimeData() {
var users = getUsers()
var time_array = [];
for (var i = 0; i < users.length; i++) {
// Logger.log(users[i].id)
var url = 'https://api.10000ft.com/api/v1/users/' + users[i].id + '/time_entries?fields=approvals' + '&from=' + from + '&to=' + to + '&auth=' + TKF_AUTH + '&per_page=' + TKF_PGSZ;
var response = UrlFetchApp.fetch(url, options);
var info = JSON.parse(response.getContentText());
var content = info.data;
var total_hours = 0;
for (var j = 0; j < content.length; j++) {
if (content[j].approvals.data.length > 0) {
total_hours += content[j].hours;
}
}
Logger.log('User name: ' + users[i].display_name + ' ' + 'User id: ' + users[i].id + ' ' + 'total hours: ' + total_hours+ ' ' + 'Email: ' + users[i].email)
}
}
function sendMail(showTimeData){
var emailAddress = user.email;
var message = 'Dear ' + user.display_name + 'Please update your details in the system'
var subject = ' Reminder';
MailApp.sendEmail(emailAddress, subject, message);
}
I was able to get a solution for this as follows:
for (var j = 0; j < content.length; j++) {
if (content[j].approvals.data.length > 0) {
total_hours += content[j].hours;
}
}
Logger.log('User name: ' + users[i].display_name + ' ' + 'User id: ' + users[i].id + ' ' + 'total hours: ' + total_hours + ' ' + 'Email: ' + users[i].email)
if (total_hours == 0) {
sendMail(users[i])
}
}
}
function sendMail(user) {
var emailAddress = user.email;
var message = 'Dear ' + user.display_name + 'Please update your details in the system'
var subject = ' Reminder';
MailApp.sendEmail(emailAddress, subject, message);
}

Angularjs code refactoring

I have my angularjs project, in this I have this code block, which is fired depending upon the dropdown selection. In this code the if and else part is mostly similar, I want to refactor the code so that the code is not repeated.
if (1 === $scope.form.type) {
response = $scope.resource.searchItemSalesInfo(params.get, params.post,function(response, headers) {
angular.forEach(response, function(row, id) {
response[id].prod_info = row.alias + ' (' + row.final_product_id + ') ';
});
$scope.totalCount = headers('x-total-count');
});
} else {
response = $scope.resource.searchOrderSalesInfo(params.get, params.post,function(response, headers) {
angular.forEach(response, function(row, id) {
response[id].prod_info = row.alias + ' (' + row.final_product_id + ') ';
});
$scope.totalCount = headers('x-total-count');
});
}
I tried to take the common functionality out in the below manner, but then the code does not works, and it breaks the functionality.
$scope.callresource = function(resourcename){
response = $scope.resource.resourcename(params.get, params.post,function(response, headers) {
angular.forEach(response, function(row, id) {
response[id].prod_info = row.alias + ' (' + row.final_product_id + ') ';
});
$scope.totalCount = headers('x-total-count');
});
}
if (1 === $scope.form.type) {
$scope.callresource(searchItemSalesInfo);
} else {
$scope.callresource(searchOrderSalesInfo);
}
Alternatively to my other reply, you could pass a reference to the function that you want to be executed in $scope.callresource ... in other words a callback function
I believe this is the more scalable approach.
$scope.callresource = function(resourceCallbackFunction){
response = resourceCallbackFunction(params.get, params.post,function(response, headers) {
angular.forEach(response, function(row, id) {
response[id].prod_info = row.alias + ' (' + row.final_product_id + ') ';
});
$scope.totalCount = headers('x-total-count');
});
}
if (1 === $scope.form.type) {
$scope.callresource($scope.resource.searchItemSalesInfo);
} else {
$scope.callresource($scope.resource.searchOrderSalesInfo);
}
You can't call the function using this syntax $scope.resource.resourcename because this syntax is telling JS to execute a function called resourcename.
Instead, try using the bracket notation:
$scope.resource[resourcename](..)
Also, when invoking the $scope.callresource function, pass the arguments as strings, because searchItemSalesInfo and searchOrderSalesInfo currently JS is trying to find them as variables.
$scope.callresource('searchItemSalesInfo');
Your code would look like this:
$scope.callresource = function(resourcename){
response = $scope.resource[resourcename](params.get, params.post,function(response, headers) {
angular.forEach(response, function(row, id) {
response[id].prod_info = row.alias + ' (' + row.final_product_id + ') ';
});
$scope.totalCount = headers('x-total-count');
});
}
if (1 === $scope.form.type) {
$scope.callresource('searchItemSalesInfo');
} else {
$scope.callresource('searchOrderSalesInfo');
}

Angular: Combine array objects and combine values

I'm retrieving data with http.get.
This provides me with an array of objects like below:
[{
"id”:12345,
"resource_state":2,
"external_id”:”abscdgrft”,
"upload_id”:1234567,
"athlete":{
"id”:123456,
"resource_state":2,
"firstname”:”testname”,
"lastname”:”testlastname”,
"profile_medium”:”image/athlete/medium.png",
"profile":"image/athlete/large.png",
"city”:”testcity”,
"state”:”teststate”,
…
},
"name”:”test name“,
"distance":87223.1,
"moving_time":11026,
"elapsed_time":11173,
"total_elevation_gain":682.3,
…
}]
I would like to combine all those object based on the athlete.firstname + athlete.lastname.
So for example all objects with athlete first name Jim and last name Donalds I want to be combined in one object, the same goes for all the other names.
When combining the objects based on the full name, values like "distance", "moving_time", "elapsed_time" and "total_elevation_gain" needs to be summed.
I tried using the code below but the problem is that I can't get it to work with multiple values like I mention above.
This is working only with one value, distance for example:
var obj = {}; // Initialize the object
angular.forEach(data, function(value, key) {
if (value.start_date > firstdayOfWeek && value.start_date < lastdayOfWeek) {
if (obj[value.athlete.firstname + ' ' + value.athlete.lastname]) { // If already exists
obj[value.athlete.firstname + ' ' + value.athlete.lastname] += value.distance; // Add value to previous value
} else {
obj[value.athlete.firstname + ' ' + value.athlete.lastname] = value.distance; // Add in object
}
} else {
//do nothing
}
});
console.log(obj); // Result
When I modify it like this it is not working anymore.
var obj = {};
angular.forEach(data, function(value, key) {
//console.log(value);
if (value.start_date > startOfLastWeek && value.start_date < endOfLastWeek) {
//console.log(value);
if (obj[value.athlete.firstname + ' ' + value.athlete.lastname]) { // If already exists
obj[value.athlete.firstname + ' ' + value.athlete.lastname] += {
"profile" : value.athlete.profile,
"distance" : value.distance,
"moving_time" : value.moving_time,
"elapsed_time" : value.elapsed_time,
"total_elevation_gain" : value.total_elevation_gain,
}; // Add value to previous value
} else {
obj[value.athlete.firstname + ' ' + value.athlete.lastname] = {
"profile" : value.athlete.profile,
"distance" : value.distance,
"moving_time" : value.moving_time,
"elapsed_time" : value.elapsed_time,
"total_elevation_gain" : value.total_elevation_gain,
}; // Add in object
}
} else {
//do nothing
}
});
console.log(obj); // Result
Thanks!
try to add item by item... You can't add all with += { ... }:
var obj = {};
angular.forEach(data, function(value, key) {
//console.log(value);
if (value.start_date > startOfLastWeek && value.start_date < endOfLastWeek) {
//console.log(value);
if (obj[value.athlete.firstname + ' ' + value.athlete.lastname]) { // If already exists
var aux = obj[value.athlete.firstname + ' ' + value.athlete.lastname];
aux.profile += value.athlete.profile;
aux.distance += value.distance;
...
} else {
obj[value.athlete.firstname + ' ' + value.athlete.lastname] = {
"profile" : value.athlete.profile,
"distance" : value.distance,
"moving_time" : value.moving_time,
"elapsed_time" : value.elapsed_time,
"total_elevation_gain" : value.total_elevation_gain,
}; // Add in object
}
} else {
//do nothing
}
});
console.log(obj); // Result
Use underscore or lodash groupBy, map and reduce
with lodash:
_.chain(myArr).map(function(o) {
return {
fullname: o.athlete.firstname + ' ' + o.athlete.lastname,
distance: o.distance,
moving_time: o.moving_time,
elapsed_time: o.elapsed_time,
total_elevation_gain: o.total_elevation_gain
}
}).groupBy(function(o) {
return o.fullaname
}).map(function(d, fullname) {
var totals = _.reduce(d, function(result, run, n) {
result.moving_time += run.moving_time | 0
result.elapsed_time += run.elapsed_time | 0
result.total_elevation_gain += run.total_elevation_gain | 0
return result
}, {
moving_time: 0,
elapsed_time: 0,
total_elevation_gain: 0
})
totals.fullname = fullname
return totals
})

How to get value for a particular row and column using node.js

I am pretty new to node.js
For my application, I am currently fetching a set of rows using the code below and iterate over each row for value of a particular column...
query = 'select ' + colName +
' from ' + rows[0].tablename +
' where ' +
'dictionaryid=' + rows[0].dictionaryid +
' and id between ' + lower + ' and ' + upper;
connection.query(query, function(err, rows1, fields) {
if (err) {
console.log(err);
throw err;
}
var contents = [];
rows1.forEach(function(elem) {
for (var key in elem) {
if (key == colName) {
contents.push(elem[key]);
}
}
});
So, in the code above, I fetch a set of rows in rows1 and iterate over all of them using a forEach.
What I want to do it to access something like rows1[i][key] i.e like 3rd column of 4th row.
How do I do it.
Any help is appreciated.
Thanks.
You could use underscore toArray to do something like this:
var _ = require('underscore');
var contents = [];
rows1.forEach(function(elem) {
contents.push(_.toArray(elem));
});
var someValue = contents[3][2]; //3rd column of 4th row.

Resources