Javascript: array.forEach() sometimes not working
this is my code snippet, where the program doesn't enter the foreach loop:
var ct = new Array();
ct["me"]= {"name" : "Jakub"};
ct["you"]= {"name" : "stack"};
ct.forEach(function (c){
document.getElementById("tmp").appendChild(document.createTextNode(c));
});
When I change the array indices from strings ("me", "you") to integers, it works:
var ct = new Array();
ct[0]= {"name" : "Jakub"};
ct[1]= {"name" : "stack"};
ct.forEach(function (c){
document.getElementById("tmp").appendChild(document.createTextNode(c));
});
Can you help me to implement the solution to iterate over the arrays with all kinds of indices? My aim is to store the values for given date objects.
I use the data for the Protovis library and AFAIK it needs an array as input.
The data structure I use in the protovis example is more complicated than this one shown above.
In my project I send via JavaBean the set of some objects. Those object contain among other things the date. My aim is to show those objects on the graph like this, presented on protovis website http:// vis.stanford.edu / protovis/ex/area.html.
I will use the horizontal axis for the time, and the vertical axis for number of objects for a given time. This is why I want to have the array sorted by the date, since AFAIK protovis only allows arrays as the data input for their diagrams in the default mode - function chaining.
edit: For now I changed the method. Instead of storing strings as array keys I do following: hereby is my original code snippet:
edit2: I added some original input: var result2 = {"h": { 10 "documents": [ 11 { 12 "biographicalCategories": [ 13 ], 14 "body": "Greece's conservative Government has ordered an investigation into a 1955 agreement between the C.I.A. and the Greek military for the establishment of a guerrilla network to fight invading Warsaw Pact forces in the event of a war.", 15 "descriptors": [ 16 ], 17 "generalOnlineDescriptors": [ 18 ], 19 "guid": 0, 20 "headline": "Greece to Investigate Plan for Guerrilla War", 21 "locations": [ 22 "GREECE" 23 ], 24 "names": [ 25 ], 26 "onlineDescriptors": [ 27 ], 28 "onlineLocations": [ 29 ], 30 "onlineOrganizations": [ 31 ], 32 "onlinePeople": [ 33 ], 34 "onlineTitles": [ 35 ], 36 "organizations": [ 37 ], 38 "people": [ 39 ], 40 "publicationDate": "1990-11-21 00:00:00.0 CET", 41 "sourceFile": "0402635.xml", 42 "taxonomicClassifiers": [ 43 ], 44 "titles": [ 45 ], 46 "typesOfMaterial": [ 47 ], 48 "score": 0.80242133 49 },
var resultTmp = new Array();
var i = 0;
var averageScore = 0;
var startDate = new Date();
var endDate = new Date(1700, 01, 01);
var docDate;
var actDate;
var tlk = new Array();
var av = 0;
var d = new Object();
result2.h.documents.forEach(function(c) {
averageScore += c.score;
if(typeof(c.publicationDate) != "undefined"){
docDate = c.publicationDate.split("-");
actDate = new Date(docDate[0], docDate[1]-1, docDate开发者_开发技巧[2].split(" ")[0]);
if(actDate endDate){
endDate = actDate;
}
if(defined(tlk[actDate])){
av = tlk[actDate];
resultTmp[av].docs.push(c);
}
else {
d = new Object();
d.date = actDate;
d.docs = new Array();
d.docs.push(c);
resultTmp[i] = d;
tlk[actDate] = i;
i++;
}
}
});
i = 0;
var dates = [];
for(key in tlk){
if(key )
d = new Date(key);
if(isValidDate(d)){
dates[i] = new Date(key);
i++;
}
}
dates.sort(function (a, b) {
return a > b;
});
var ii = 0;
i = 0;
var ddocs;
var result = new Array();
for(i=0; i maxDocsPerDate){
maxDocsPerDate = d.docs.length;
}
result[i] = d;
}
edit3 the code above is working now:
In a nutshell: I use the tlk array to reflect the date to the index. For one index in the resultTmp array I store the date and the set of objects related to that date. The next part of code I use to sort the dates from the oldest to the newest and analogously sort the resultTemp. The sorted version of resultTemp is in the result Array.
I present data in protovis in following way:
vis.add(pv.Line)
.data(result)
.lineWidth(2)
.left(function(a) x(a.date))
.bottom(function(a) y(a.docs.length))
.add(pv.Dot)
.lineWidth(function(a) a.docs.length - (a.docs.length-1)/3)
.radius(function(a) a.docs.length * (a.docs.length/1.2))
.fillStyle(function(a) color(a.docs.length))
.event("click", function(a) Popup.show(a.docs))
.anchor("top").add(pv.Label)
.text(function(a) a.docs.length)
.textBaseline("bottom");
vis.render();
The exemplary result looks like: i.imgur.com / WODYA.png
I didn't include the code for printing the x and y axes as well as for scaling from the date to the graph width. You can find examples on the protovis examples page.BTW: I'm confused why in the part:
for(key in tlk){
dates[i] = new Date(key);
i++;
}
As the last key I get "contains"? Tried to find answer on Internet, without success. Bears explained in his comment that the reason I get this probelm is because I'm iterating over the properties of the array.
JavaScript arrays only support indexing by number. When you write
ct["me"]= {"name" : "Jakub"};
ct["you"]= {"name" : "stack"};
you are adding ad-hoc properties me
and you
to the array; you are not adding elements to the array. (Also using the new Array()
constructor is kind of weird. If you want an array, use the []
literal.)
It sounds like you should use a JavaScript object, not an array, but be mindful that they only support string indices.
var ct = {};
ct['me'] = {name: 'Jakub'};
ct['you'] = {name: 'stack'};
for (var k in ct) {
document.getElementById('tmp', appendChild(document.createTextNode(ct[k]));
}
Edit: If you want to treat the horizontal axis as time, you really don't need to do much more work. There's a good basic example here; view the page source to see the code. The trick here is that, while the data really is an array (of objects), the x-coordinate is stated explicitly as a property rather than as the index in the data array. Each element in the array looks something like this:
>>> data[0]
{x: /* some JavaScript date object */, y: /* some number */ }
Sources:
- http://www.google.com/search?q=protovis+x-axis+time
- http://groups.google.com/group/protovis/browse_thread/thread/75ec1518692c2de5
- http://graphics.stanford.edu/~mbostock/dates/dates.html
Edit 2: You still seem mixed up about arrays versus objects.
Regarding your "BTW": when you write for(key in tlk) ...
you're iterating over the keys that are already in the array. That's treating the array as an object, and that's not what you want! You see contains
because you're iterating over properties of an array, and contains
is a function attached to every array (are you using prototype, or another similar library?).
The basic problem, however, is that you're indexing into an array (tlk
) using a Date. That's a big no-no; even if tlk
is an object, because you can only index objects using strings. I really don't get what you're doing with tlk
, but I don't think you need it at all. What's the form of your input data? If you can give me a small example of the input, I can probably show you what to do with it.
Also, you should really use array and object literals rather than the Array
and Object
constructors. For example, use var tlk = []
rather than var tlk = new Array();
, and var d = {};
rather than var d = new Object();
.
JavaScript doesn't have associative arrays, par se. Objects have named properties, though, which is similar. forEach() will only iterate over indexed properties. A for...in
loop will help you here, although in general you would avoid using for...in
on arrays because it iterates over named properties too.
for (var c in ct) {
if (ct.hasOwnProperty(c)) {
// do something
}
}
See also:
- hasOwnProperty() - MDC
- for...in - MDC
- JavaScript Associative Arrays - persistent.info
The Array.prototype.forEach
method traverses the array on its numeric indexes.
Arrays are not 'associative', if you want to have named properties with values you should use a simple object, and use the for-in
statement to enumerate the existing properties:
var ct = {};
ct["me"]= {"name" : "Jakub"};
ct["you"]= {"name" : "stack"};
for (var prop in ct) {
if (ct.hasOwnProperty(prop)) {
alert(ct[prop]);
}
}
The hasOwnProperty
method is called because the for-in
statement traverse properties that are inherited, in that way it will enumerate only the properties that physically exist on the object (own properties).
You can use if (Object.prototype.hasOwnProperty.call(ct, prop))
instead of if (ct.hasOwnProperty(prop))
for extra safety, because if the object has a property named "hasOwnProperty
", will not be the method you want to execute.
精彩评论