views:

114

answers:

3

Hi,

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.

+1  A: 

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:

Andy E
Thanks for your reply. I check if it is possible to connect that solution with the other parts of my code. I use the data for the protovis library and AFAIK it needs an array as input.
Jakub
@Andy: I have to disagree with the title of that article from Ajaxian, since it's pretty misleading. In this context, "object," "hash," and "associative arrays" are all synonymous. The article even states "use Object for associative arrays, and use Array for numeric arrays" - I think that if you don't have the understanding already, the article is just confusing.
Matt Ball
@Bears: yeah, I agree with you. Usually I try and stay away from "*x* is considered harmful" links because not very many things are actually harmful in the right hands. It was the best link I plucked from the first page of Google results, if I'm honest. Swapped it out for something else.
Andy E
A: 

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:


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();.

Matt Ball
Thanks for your reply. I check if it is possible to connect that solution with the other parts of my code. I use the data for the protovis library and AFAIK it needs an array as input.
Jakub
@Jakub: would you mind updating your question with an example of how you'd like to use Protovis with your input?
Matt Ball
@Bears: I added the description why I need this in protovis. I added some links where you can see the example I try to reproduce given my data.
Jakub
@Jakub: thanks. See my edit - it shouldn't be too hard to do what you want.
Matt Ball
Thanks for your reply. I will take your edit into account. I didn't write it before but I don't only need the number of objects for a given time, but also some reference to these objects. when I click on the line for a given time I want to see the objects.Regarding the problem with hyperlinks:While I'm trying to edit the main question field, I can't do this, because it contains more than one link and I get the message:"Oops! Your edit couldn't be submitted because:* we're sorry, but as a spam prevention mechanism, new users can only post a maximum of one hyperlink. Earn 10 reputation to..
Jakub
@Jakub: As far as grabbing references to your objects: that's really not an issue; it's all in how you populate your data array. You don't need to fill it the way the example does, but the final array should have a similar structure.
Matt Ball
@Bears:I appreciate your help!! I saw your edit2 after I did my task working. I updated the question with the part of the input data. I also included the link to the output image I create with protovis for this example.
Jakub
+1  A: 

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.

CMS
Thanks for your reply. I check if it is possible to connect that solution with the other parts of my code. I use the data for the protovis library and AFAIK it needs an array as input.
Jakub