I'm writing a function that should read a csv file and then process it so I can use it to generate an HTML table. Basically the output of the function should be a multidimensional array that looks like this : [["prop", "type"], ["prop", "type"]...]
The problem is that when e.g. I try to access to "prop" using importProps[0][0], it will actually output a "p". The weirdest thing is the output of the console in the two last lines of the function; I consider them as being the same thing, but they deliver a different output. I don't understand why I can't access to the whole string like on the console.log(elem[0]) line.
Here is my code.
function importProperties() {
//read the data
var importProps = readTextFile(getProjectDirectory() + '/test.csv'); //outputs ["prop; type", "prop; type"...]
// process it to become [["prop", "type"], ["prop", "type"]...]
for (var i = 0; i < importProps.length; i++) {
var elem = importProps[i].split(';');
importProps[i] = elem;
console.log(elem[0]); // Outputs $"prop"
console.log(importProps[i][0]); // Outputs $"p" <--WHY ?
}
My original loop was
for (var i = 0; i < importProps.length; i++) {
importProps[i] = importProps[i].split(';');
}
which produces the same unexpected result. I just added the element variable for debugging purposes.
By the way, I'm writing this in a software-embedded browser which communicates with the software through a provided API. The console in this browser has only very limited capabilities and makes it hard to do some proper debugging.
Unfortunatelly I am not allowed to comment :(
The code runs fine in console and provides what you asked for.
My guess would be that in the real code you assigned the string instead of the array importProps[i] = elem[0];
var importProps = ["prop; type", "prop; type"];
for (var i = 0; i < importProps.length; i++) {
var elem = importProps[i].split(';');
importProps[i] = elem[0]; //Maybe you have this line live instead of importProps[i] = elem;?
console.log(elem[0]);
console.log(importProps[i][0]);
}
I finally found the solution. The browser that is embedded in the software is actually based on Webkit. After I've tried pasting the code in Firefox as suggested by Mike, I got to the same conclusion as he did : it worked perfectly.
After a little research and multiple typeof statements, I found where the problem came from. It actually came from the importProps[i] = elem; line. elem is an array here (typeof outputs object) while importProps[i] is a string (typeof outputs string). After I assign elem to importProps[i], importProps[i] is still a string ! (typeof returns string).
for (var i = 0; i < importProps.length; i++) {
var elem = importProps[i].split(';');
importProps[i] = elem;
console.log(typeof importProps[i]); // Logs 'string'!!! Firefox logs 'object' here
console.log(typeof elem); // Logs 'object'
};
That is not the case in Firefox, and I don't know why this behavior has been implemented to Webkit. The solution was finally to create a multidimensional array and assign the split string to it.
function importProperties() {
//read the data ["prop;type", "prop;type"]
var importProps = xxxAPI.readTextFile(xxxAPI.getProjectDirectory() + '/test.csv');
//process to be [["prop", "type"], ["prop", "type"]...]
var importPropsArray = [[]]
for (var i = 0; i < importProps.length; i++) {
var elem = importProps[i].split(';');
importPropsArray[i] = elem;
};
return importPropsArray;
};
I don't know if this is kind of a bug or a desired behavior, but it's very weird.
Related
I've been having a hard time with cross browser compatibility and scrapping the dom.
I've added data analytics tracking to ecommerce transactions in order to grab the product and transaction amount for each purchase.
Initially I was using document.querySelectorAll('#someId')[0].textContent to get the product name and that was working fine for every browser except internet explorer.
It took some time to figure out that it was the .textContent part that was causing ie problems.
Yesterday I changed .textContent to .innerText. From looking inside analytics it seems that the issue has been resolved for ie but now Firefox is failing.
I was hoping to find a solution without writing an if statement to check for the functionality of .textContent or .innerText.
Is there a cross browser solution .getTheText?
If not what would be the best way around this? Is there a simple solution? (I ask given my knowledge and experience with scripting, which is limited)
** added following comments **
If this is my code block:
// build products object
var prods = [];
var brand = document.querySelectorAll('.txtStayRoomLocation');
var name = document.querySelectorAll('.txtStayRoomDescription');
var price = document.querySelectorAll('.txtStayRoomSplashPriceAmount');
for(var i = 0; i < brand.length; i++) {
//set granular vars
var prd = {};
//add to prd object
prd.brand = brand[i].innerText;
prd.name = name[i].innerText;
prd.price = price[i].innerText;
prd.quantity = window.session_context_vars.BookingContext.Booking.ReservationLineItems[i].ReservationCharges.length/2;;
//add to prods array
prods.push(prd);
}
Then if I understand the syntax from the comments and the question linked to in the comment, is this what I should do:
// build products object
var prods = [];
var brand = document.querySelectorAll('.txtStayRoomLocation');
var name = document.querySelectorAll('.txtStayRoomDescription');
var price = document.querySelectorAll('.txtStayRoomSplashPriceAmount');
for(var i = 0; i < brand.length; i++) {
//set granular vars
var prd = {};
//add to prd object
prd.brand = brand[i].textContent || brand[i].innerText;
prd.name = name[i].textContent || name[i].innerText;
prd.price = price[i].textContent || price[i].innerText;
prd.quantity = window.session_context_vars.BookingContext.Booking.ReservationLineItems[i].ReservationCharges.length/2;;
//add to prods array
prods.push(prd);
}
So using or with a double bar || assigns the first non null value?
Re: your edit, not quite. The way to access methods or properties on an object (eg a DOM element) is to use dot notation if you have the name itself, or square brackets in case of variables/expressions (also works with strings, as in obj["propName"], which is equivalent to obj.propName). You can also just test the property against one element and use that from there on:
// build products object
var prods = [];
var brand = document.querySelectorAll('.txtStayRoomLocation');
var name = document.querySelectorAll('.txtStayRoomDescription');
var price = document.querySelectorAll('.txtStayRoomSplashPriceAmount');
for(var i = 0; i < brand.length; i++) {
//set granular vars
var prd = {};
//add to prd object
var txtProp = ("innerText" in brand[i]) ? "innerText" : "textContent"; //added string quotes as per comments
prd.brand = brand[i][txtProp];
prd.name = name[i][txtProp];
prd.price = price[i][txtProp];
prd.quantity = window.session_context_vars.BookingContext.Booking.ReservationLineItems[i].ReservationCharges.length/2;;
//add to prods array
prods.push(prd);
}
Regarding the line:
var txtProp = (innerText in brand[i]) ? innerText : textContent;
The in keyword checks an object to access the property (syntax: var property in object). As for the question notation (I made an error earlier, using ||, the correct thing to use was a :),
var myVar = (prop in object) ? object[prop] : false;
As an expression, it basically evaluates the stuff before the ?, and if it's true, returns the expression before the :, else the one after. So the above is the same as / a shorthand for:
if(prop in object){
var myVar = object[prop];
}
else{
var myVar = false;
}
Since you are checking between two properties only and wanting to assign one or the other, the shortest way would indeed be:
var txtProp = brand[i].innerText || brand[i].textContent;
It would basically test the first property, and if it were false or undefined, it would use the second one. The only reason I (pedantically) avoid using this is because the first test of a || b would fail even if a existed but just had a value of 0, or an empty string (""), or was set to null.
I tried to parse the color-names on this page: http://www.google.com/design/spec/style/color.html#
Using this code:
var all = document.querySelectorAll(".color-group");
for(var i=0; i<all.length; i++){
var e = all[i];
var name = e.querySelector('span.name');
console.debug(name.innerHTML);
}
However the printed result is always undefined.
This slightly changed code however works:
var all = document.querySelectorAll(".color-group");
for(var i=0; i<all.length; i++){
var e = all[i];
var name = e.querySelector('span.name').innerHTML;
console.debug(name);
}
The only difference is that I access the result of querySelector directly and not via the name-variable.
I tried it with Chrome, Safari and Firefox which all did not return the color-names. IE however manged to get it right this time.
Is this a general bug or feature or is it a problem with the website?
If you're running that code in the global scope, the variable name conflicts with that of window.name (which is a string); consider creating a scope:
(function() {
var all = document.querySelectorAll(".color-group");
for(var i=0; i<all.length; i++){
var e = all[i];
var name = e.querySelector('span.name');
console.debug(name.innerHTML);
}
}());
Or, just run that code inside a regular named function and call that from the global scope.
It seems there is something special about variable name.
var name = 3; name
// => "3"
var dame = 3; dame
// => 3
This behaviour is exhibited even by a blank tab (in Chrome at least). If you name your variable something else, it will go away. I believe (!) the reason is that you're executing in console, and name always refers to window.name; it goes away if you run it in a script; but I am not 100% sure my explanation is the correct one.
I have an array of object which i want to convert to a JSON string but i recieve the following error: JavaScript exception :Uncaught TypeError: Converting circular structure to JSON
From what i understand this is because there is a loop in references. i've searched here on SO for solutions and someone came up with a 'replace function'. this looked something like this:
var cache = [];
JSON.stringify(o, function(key, value) {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) {
// Circular reference found, discard key
return;
}
// Store value in our collection
cache.push(value);
}
return value;
});
cache = null; // Enable garbage collection
but using this gives me the following error: Maximum call stack size exceeded.
i think this solution doesn't really fits my needs, because i am not intrested in all the elements ofthe array that i'm trying to convert. These elements are added are 3rd party(google maps) so i have no influence on their child objects.
This is a screenshot of my array object:
i am only interested in the following items:
Tour
-id
-name
-days :
-id
-name
-markers :
-dayId
-title
-position :
-nb
-ob
-thumbs :
- 0
- 1
- ...
because the object array is created by several functions/services/factories is it difficult to make a fiddle or provide some code samples.
Any sugestion how to convert an array like this to JSON is welcome, thanks in advance
For anyone interested, i ended up making my own array with just the elements that i needed:
function CreateJson(tour){
var sJson = {};
sJson.name = tour.name;
sJson.id = tour.id;
sJson.days = [];
sJson.markers = [];
sJson.thumbs = [];
for(i = 0; i < tour.days.length; i++){
sJson.days.push({
id: tour.days[i].id,
name: tour.days[i].name
});
for(j = 0; j < tour.days[i].markers.length; j++){
sJson.markers.push({
id: tour.days[i].markers[j].id,
dayId: tour.days[i].markers[j].dayId,
name: tour.days[i].markers[j].title
});
}
for(k = 0; k < $scope.thumbs.length; k++){
sJson.thumbs.push($scope.thumbs[k])
}
};
The following is my code, i intend to use it as a bookmarklet.
javascript: (function () {
var jsCode = document.createElement('script');
jsCode.setAttribute('src', '//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js');
document.body.appendChild(jsCode);
console.log("Everything is fine upto here!");
data = [["#txtApplcntName","test"],["#txtApplcntAdd","test"]];
console.log(data);
var obj = $.parseJSON(data);
for (var i = obj.length - 1; i >= 0; i--) {
var current = obj[i];
console.log(current);
$(current[0]).val(current[1]);
};
})();
Problems start when the actions in the for loop never take place. It gets even weirder when i can successfully log the variable obj and it logs, but when i do obj.length a null is encountered?
I am experimenting on Google chrome
Try this:
data = '[["#txtApplcntName","test"],["#txtApplcntAdd","test"]]';
Method JSON.parse(str, reviver) reads a string.
What you're trying to do is treat an ordinary array as JSON.
var obj = $.parseJSON(data)
obj get a null ."data" can't convert to json object.
var AppPatientsList = JSON.parse(JSON RESPONSE);
var AppPatientsListSort = AppPatientsList.sort(function(a,b){
return a.firstName.toLowerCase() <b.firstName.toLowerCase()
? -1
: a.firstName.toLowerCase()>b.firstName.toLowerCase()
? 1 : 0;
});
var DataArray = [];
for (var i = 0; i < AppPatientsListSort.length; ++i) {
if (AppPatientsListSort[i].firstName === search.value) {
var appointment = {};
appointment.PatientID = AppPatientsListSort[i].PatientID;
appointment.ScheduleDate = AppPatientsListSort[i].ScheduleDate;
alert(appointment.ScheduleDate); // Works fine, i get the date...
}
DataArray[i] = appointment;
}
var RowIndex = 0;
var ScheduleDate = "";
for (i = 0, len = DataArray.length; i < len; i++) {
// Throws me error in this place... WHY?
if (ScheduleDate != DataArray[i].ScheduleDate) {
ScheduleDate = DataArray[i].ScheduleDate;
}
}
What's wrong with this code, why i am not able to access the ScheduleDate?
You are only initializing the appointment variable when you are inside the if clause, but you are adding it to the array on every iteration.
If the first element of AppPatientsListSort does not have the value you search for, DataArray[0] will contain undefined.
In the second loop you then try to access DataArray[0].ScheduleDate which will throw an error.
Update:
Even more important, as JavaScript has no block scope, it might be that several entries in DataArray point to the same appointment object.
Depending on what you want to do, everything it takes might be to change
DataArray[i] = appointment;
to
DataArray.push(appointment);
and move this statement inside the if clause so that only appointments are added that match the search criteria.
Further notes: To have a look what your DataArray contains, make a console.dir(DataArray) before the second loop and inspect the content (assuming you are using Chrome or Safari, use Firebug for Firefox).