Community Pick: Many members of our community have endorsed this article.
Editor's Choice: This article has been selected by our editors as an exceptional contribution.

Collections of Records in JavaScript

DanRollins
CERTIFIED EXPERT
Published:
Updated:
When you need to keep track of a simple list of numbers or strings, the Array object is your most direct tool.  As we saw in my earlier EE Article, typical array handling might look like this:
var aNames= [ "Adam", "Bill", "Carl" ]; // create an array
                      
                      alert( aNames[0] ); // Adam  --  look it up by its index number
                      
                      // iterate through them
                      for ( var j=0; j< aNames.length; j++ ) {
                          alert( aNames[j] );  // 
                      }
                      

Open in new window

But JavaScript also supports Associative Arrays (aka Maps or Dictionaries) -- providing a way to access list items not just by position, but by an associated key.
The Associate Array is a "bag of properties" of the base Object objectHowever, this support might not work exactly the way that you might think it should...  
// this seems reasonable...
                      
                      var aNames= new Array();  
                      aNames[ "Anderson" ]= "Adam";
                      aNames[ "Browne"   ]= "Bill";
                      aNames[ "Crawford" ]= "Carl";
                      
                      alert( aNames[ "Anderson" ] );  // shows Adam (as expected)
                      
                      // ... but this might not seem reasonable:
                      
                      alert( aNames.length ); // shows 0 !!! (why not 3?)
                      

Open in new window

The above code creates an Array and then puts nothing into any of the array elements!

Here's what's going on:  Every JavaScript variable (including Array variables) is an object with the underlying features of the Object object.  It is that base class that supports the key/value pair handling of associative arrays.  So, for instance:
var mapNames= new Object();     // or:  var mapNames= {};
                      mapNames[ "Anderson" ]= "Adam";
                      mapNames[ "Browne"   ]= "Bill";
                      mapNames.Crawford     = "Carl"; // alternate syntax
                      mapNames[ 2          ]= "Two";
                      
                      alert( mapNames.length ); // undefined (it is not an Array)
                      
                      for ( var j in mapNames ) { // show the keys
                          alert( j );             // Anderson, Browne, Crawford, 2
                      }
                      for ( var j in mapNames ) { // show the data associated with each key
                          alert( mapNames[j] );   // Adam, Bill, Carl, Two
                      }
                      

Open in new window

These key/value pairs are often called properties of the object.

The Object object supports a syntax option that allows you to define the key and the value at the same time:
var mapNames= {
                         "Anderson" : "Adam",    // syntax is key:value
                         "Browne"   : "Bill",
                         "Crawford" : "Carl"
                      };
                      for ( var n in mapNames ) {        // show the keys and values
                          alert( n+ "=" + mapNames[n] ); // Anderson=Adam, Browne=Bill, etc...
                      }
                      

Open in new window

The key is on the left and the value is on the right.  The value does not need to be a string or other simple value.  It can be an array, or it can be another object that has its own map of key/value pairs (properties).  You can make the object as complex as you want.  See JSON for some related information and examples.


An Object as a Function Parameter

This flexibility makes it very useful for many things, one of which is passing data to a function call in such a way that the function can refer to the parameters by name.  You've probably seen this syntax:
...
                      DoThat( {color:"Red", font:"Arial" } ); // unnamed object
                      ...
                      function DoThat( o ) {
                          alert( o.color );    // shows:  Red
                          alert( o.font );     // shows:  Arial
                      }
                      

Open in new window

The caller creates an anonymous object (a map of key:value pairs) and the called function can then access the values by name.  Note the shortcut used in the call:  If the key is a text string, you can omit the quotes when you identify the property.


Creating an Associative Array 'Record'

You can create a map object on-the-fly, adding new properties at whim.  This flexibility has a practical programming downside; for example:
var o= {};              // or: var o= new Object();
                      o.lastName= "Anderson";
                      o.age= 17;
                      
                      ... later that day ...
                      
                      o.LastName= "Smith";  // oops, uppercase L
                      

Open in new window

If you misspell a key name, then you have just added a new property to the object.  That's probably not what you wanted to do!  Though JavaScript associative maps don't need to be declared in advance, doing so can help you to avoid some errors.  Let's look at a way of "formalizing" the object properties.

It is often the case that you want to predefine a record -- a structure that contains all of the data you want to be contained in that object.  You'll want to be able to refer to the definition of that object so that you can see all of its properties (and, for one thing, make sure that you spell them right each and every time you use one).  

At the same time, you probably want to provide a way to initialize such a record.  You'll want to set default values and provide a way to populate it easily.  Enter, stage right, the JavaScript constructor syntax:
function PersonRec() {
                          this.sNameLast=  '';   // the "this." part is required
                          this.sNameFirst= '';
                          this.nAge=       0;
                          this.fIsParent=  false;
                          this.asChildren= new Array();
                      }
                      

Open in new window

Here we have defined an object with a set of specific properties.   When I get to this stage, I start to think of the object as a record with fields.

Coming from a C++ background, I thought it strange to use the function keyword as the means to define a structure, but it makes sense in the context that it is actually a constructor function.  The above example accepts no parameters, but the following one accepts a variable number of parameters and assigns them to the record fields.
function PersonRec(p1,p2,p3,p4,p5) {
                          this.sNameLast= (p1 != undefined) ? p1 : '';
                          this.sNameFirst=(p2 != undefined) ? p2 : '';
                          this.nAge=      (p3 != undefined) ? p3 : -1;
                          this.fIsParent= (p4 != undefined) ? p4 : false;
                          this.asChildren=(p5 != undefined) ? p5 : new Array();
                      }
                      

Open in new window

That version accepts a variable number of parameters.  Missing parameters are detected and the corresponding fields will be assigned default values.  To create an instance of the record, use syntax like:
var rPer1= new PersonRec();  // populated with defaults
                      
                      var rPer2= new PersonRec( "Anderson", "Adam", 35, true, ["Andy","Al"] ); // all data
                      
                      var rPer3= new PersonRec( "Browne", "Bill" );  // with some data and some defaults
                      
                      rPer3.nAge= 43;              // update the record
                      rPer3.sNameFirst= "William"; // update the record
                      

Open in new window

The constructor can do a lot more than just assign values.  It can calculate fields based on other fields, or pre-populate a lookup table, or read and parse data from a webpage, or grab data from a database  -- any kind of initialization that you want to have happen each time an instance of the record object is created.

What's more, the record does not need to just house data.  As with any object-oriented programming, you can provide methods (functions) that will apply to the associated data.  For instance, we could add:
function PersonRec(p1,p2) {
                          this.sNameLast= p1;
                          this.sNameFirst=p2;
                          ... 
                          //-------------------------------- add some methods; i.e., member functions
                          this.GetFullName= function() {
                             return( this.sNameFirst +" "+ this.sNameLast ); 
                          };
                      
                          this.toString= function() { 
                              var s=  "Name:  " + this.GetFullName() + "\n";
                              s+=     "Age:   " + this.nAge          + "\n";
                              if ( this.fIsParent ) {
                                  s+= "Children: "    + this.asChildren;
                              }
                              return( s );
                          };
                      }
                      

Open in new window

The Object.toString() member already exists, but it just outputs [Object object], which is not too useful.  It's handy to write an override function like that shown, so that you can get a quick display of the data for easy debugging.


Using an Associative Array 'Record'

An object like the one we've defined here is most often used as one item in an array of such records.  This is the classic collection concept.  Let's say that you had a dropdown list of options, and when the user selects one, you would want to populate a set of input fields on a form.

Your JavaScript might populate the collection by using AJAX, or parsing a web page, or reading from a disk file.  Or you might pre-populate an array in the source code:
var arPersons = [
                         new PersonRec("Anderson", "Adam", 35, true, ["Andy","Alice"] ),
                         new PersonRec("Browne",   "Bill", 25, false ),
                         new PersonRec("Crawford", "Carl", 45, true, ["Caroline"] )
                      ];
                      

Open in new window

Now that we have defined an array of these records, we can easily cycle through them using JavaScript array syntax:
var oCombo= document.getElementById('selectName'); // a <SELECT> element
                      
                      oCombo.options.length=0;                // clear out the box
                      oCombo.options.length=arPersons.length; // prepare to repopulate
                      
                      for (var j=0; j<arPersons.length; j++) {
                         oCombo.options[j].text= arPersons[j].sNameLast;
                      }
                      
                      ... later, see what's been selected ...
                      
                      var i= oCombo.selectedIndex;
                      alert( arPersons[i].sNameFirst +" is "+ arPersons[i].nAge );
                      

Open in new window

And to generate a table like this:
Output a simple table from the collection...just cycle through the array elements and display the PersonRec object attribute values like so:
function BuildOutputTable() {
                          var sOut="<table border=2>";
                          sOut +="<tr>";
                          sOut +="<td>Last Name</td><td>First Name</td><td>Age</td><td># of Kids</td>"
                          sOut +="</tr>";
                          for ( var j=0; j<arPersons.length; j++ ) {
                              var rP= arPersons[j];  // a PersonRec object
                              sOut += "<tr>";
                              sOut += "<td>" +rP.sNameLast+         "</td>";
                              sOut += "<td>" +rP.sNameFirst+        "</td>";
                              sOut += "<td>" +rP.nAge+              "</td>";
                              sOut += "<td>" +rP.asChildren.length+ "</td>";
                              sOut += "</tr>";
                          }
                          sOut += "</table>";
                          return(sOut);
                      }
                      

Open in new window


Summary

In this article:

We looked at the JavaScript Object object and its built-in functionality for handling an associative array.
We took special notice of the differences between array data (indexed by a number) and object properties (indexed by a key value).
One useful ability of an associative array is that it lets you pass parameter data to a function in a very flexible way.
We saw how to create a constructor function as a means to formalize the elements in the object and to do initialization.  We saw how to add methods (functions) to such a record.
Finally, we created and accessed an array of Objects, treating them as a set of records.
In the next installment, we will look at how to sort and do other manipulations of this type of JavaScript record.


References

Related Articles by Dan Rollins:

   Sorting Arrays and Collections in JavaScript
   2D Arrays in JavaScript
   HTA - Hypertext Application tutorial
   Convert string to number / Convert number to string in JavaScript
   Debugging JScript Programs with Visual Studio 2008

The following webpages contain useful information on related topics:

    Objects as associative arrays
    Mastering Javascript Arrays
    Mastering JSON ( JavaScript Object Notation )

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
If you liked this article and want to see more from this author, please click the Yes button near the:
      Was this article helpful?
label that is just below and to the right of this text.   Thanks!
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
4
22,928 Views
DanRollins
CERTIFIED EXPERT

Comments (3)

Michel PlungjanIT Expert
CERTIFIED EXPERT
Distinguished Expert 2022

Commented:
Alternative:


function PersonRec(p1,p2,p3,p4,p5) {
  this.sNameLast   =  p1 || "";
  this.sNameFirst  =  p2 || "";
  this.nAge        = (p3 || (p3===0)?0:-1; // okeee - perhaps not elegant
  this.fIsParent   = (p4 || (p3===true)?true:false; // nor here
  this.asChildren  =  p5 || [];
}

Open in new window

CERTIFIED EXPERT
Author of the Year 2009

Author

Commented:
Ah, yes, the cryptic OR-op-idiom technique which leverages the fact that that "undefined" equates to "false" so the second part executes only if the first has no value.  I shy away from syntax that is prone to parentheses mistakes and/or takes deep mental calculus to read ... especially in tutorial articles.  Even the "ternary" x?y:z was a break from the simple code that I really like :-)

=====================
One issue I hit on that "constructor issue" (and did not discuss in the article) is what happens when the caller skips a parameter in the middle of the list:

     PersonRec("Rollins", "Dan", ,true, ['Ace','Spike'] ) ;

It seems to be a JavaScript compile-time syntax error to use two consecutive commas that way in a function call.  I guess it's like with C++ where one needs to put the seldom-used parameters at the end.  One could actually send in the string 'undefined', I suppose.
Michel PlungjanIT Expert
CERTIFIED EXPERT
Distinguished Expert 2022

Commented:
I would send null

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.