Click here to Skip to main content
Click here to Skip to main content

Tagged as

Go to top

Easy JavaScript Generic List Implementation

, 5 May 2012
Rate this:
Please Sign up or sign in to vote.
A quick and easy JavaScript implementation of a Generic List with LINQ support.

Introduction

Generic collections in .NET, in combination with LINQ, are powerful tools for the C# or VB.NET developer; however, nothing like it comes with JavaScript. This code provides the beginnings of a JavaScript implementation for a Generic List. Fortunately, due to JavaScript's flexibility, this can be achieved with a relatively small amount of code. 

Using the code 

The following public functions with a description of each are currently supported:

  • Add: Add an element to the end of the list.
  • ElementAt: Get the element at a specific index in the list.
  • Where: Return a copy of this List object with only the elements that meet the criteria.
  • FirstOrDefault: Return the first object in the list that meets the 'query' criteria or null if
  • Count: Return the number of elements in the list. 
  • OrderBy: Order (ascending) the objects in the list by the given object property name.
  • OrderByDescending: Order (descending) the objects in the list by the given object property
  • Data: Set the list data using the passed in array. 

Example of using Car objects to fill the List:

function Car(make, model)
{
    this.make = make;
    this.model = model;
}

var myList = new List();
myList.Add(new Car("Honda", "CR-V"));
myList.Add(new Car("Nissan", "Sentra"));
myList.Add(new Car("Honda", "Civic"));

var selList = myList.Where("make == 'Honda'").OrderByDescending("model");

History

Version 1 uploaded on May 4, 2012.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Shawn Lawsure
Software Developer (Senior) Diversified Business Communications
United States United States
For over 20 years I have worked in the Information Systems field as a full-time employee for a variety of local companies. Throughout this time I have had the privilege to work with many different technologies and languages from COBOL on IBM mainframes to C# in Silverlight. My most recent work has been developed using the entire .NET Framework stack including Windows Presentation Foundation, Windows Workflow Foundation, and Windows Communication Foundation. Prior to this I spent many years upgrading a COM and ASP-based application written in VB6 to .NET. In nearly all of the work I have been involved with in the past ten years I have played a lead role in the design as well as the development of the work.
 
Specialties
.NET including WPF, WCF, WWF, and C#.

Comments and Discussions

 
GeneralMy vote of 5 PinmemberS V Saichandra30-Aug-12 1:36 
QuestionUpdated again Pinmemberjfriedman14-May-12 21:59 
GeneralMy vote of 3 Pinmemberjfriedman6-May-12 5:49 
QuestionI made some improvements Pinmemberjfriedman6-May-12 5:49 
I like the style and I made some improvements which I believe make it act more like the List in .Net.
 
I have also added a Contains Method and Distinct Method. I have made several Code Improvements such as Instance Tracking, Public Properties, Destructor support and Managed the object prototype to ensure memory is reasonable.
 
The only further improvement I would be able to see would be the addition of the Array.prototype members to be available from the list either by alias et al.
 
 
/*******************************************************************************************
   Title:  Javascript Generic List Implementation
   Description:  An implementation of a Generic List with LINQ support (based off .NET).
   Author:  Julius Friedman / Shawn Lawsure
   Usage Example:
 
        function Car(make, model)
        {
            this.make = make;
            this.model = model;
        }
 
        var myList = new List();
        myList.Add(new Car("Honda", "Civic"));
        myList.Add(new Car("Nissan", "Sentra"));
        myList.Add(new Car("Honda", "Cr-V"));
        myList.Add(new Car("Honda", "Cr-V"));
 
        var selList = myList.Where("make == 'Honda'").OrderByDescending("model").Distinct();
         
*********************************************************************************************/
 
function List() {
 
    // ===============  Private Attributes  =================================================

    var oType = undefined;      // Used to ensure that all objects added to the list are of the same type.
    var listArray = [];         // Stores all the list data.

    //If supported
    if (this.__defineGetter__) {
 
        // Public Properties
        this.__defineGetter__("array", function () {
            return listArray;
        });
 
        this.__defineSetter__("array", function (val) {
            listArray = val;
        });
 
        this.__defineGetter__("$key", function () {
            return key;
        });
    };
 
    // ===============  Private Methods  ====================================================

    // Method:  copy
    // Description:  Create a copy of this List object using the passed in array of data.
    function copy(array)
    {
        var newList = new List();
        for (property in this)
            newList[property] = this[property];
 
        newList.array = array;
 
        return newList;
    }
 
    // Method:  validate
    // Description:  Make sure that all objects added to the List are of the same type.
    function validate(object)
    {
        if (typeof(object) != oType)
            throw "Only one object type is allowed in a list";
    }
 
    // Method:  select
    // Description:  Return a copy of this List object with only the elements that meet the criteria
    //               as defined by the 'query' parameter.
    // Usage Example:  
    //              var selList = select("make == 'Honda'").
    function select(query)
    {
        var selectList = copy([]);
 
        for (var arrIndex = 0; arrIndex < listArray.length; arrIndex++)
            with (listArray[arrIndex])
                if (eval(query))
                    selectList.Add(listArray[arrIndex]);
 
        return selectList;
    }
 
    // Method:  genericSort
    // Description:  Sort comparison function using an object property name.  Pass this function to
    //               the Javascript sort function to sort the list by a given property name.
    // Usage Example:
    //              var sortedList = listArray.sort(genericSort('model'));
    function genericSort(property)
    {
        return function (a, b)
        {
            return (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
        }
    } 
 
    // ===============  Public Methods  ======================================================

    // Method:  Add
    // Description:  Add an element to the end of the list.
    this.Add = function(object)
    {
        if (!oType)
            oType = typeof(object);
 
        validate(object);
 
        listArray.push(object);
    }
 
    // Method:  ElementAt
    // Description:  Get the element at a specific index in the list.
    this.ElementAt = function(index)
    {
        if (index >= this.Count() || index < 0)
            throw "Invalid index parameter in call to List.ElementAt";
        return listArray[index];
    }
 
    // Method:  Where
    // Description:  Return a copy of this List object with only the elements that meet the criteria
    //               as defined by the 'query' parameter.
    this.Where = function (query)
    {
        return select(query);
    }
 
    // Method:  FirstOrDefault
    // Description:  Return the first object in the list that meets the 'query' criteria or null if
    //               no objects are found.
    this.FirstOrDefault = function (query)
    {
        var list = select(query);
        return list ? list.ElementAt(0) : null;
    }
 
    // Method:  Count
    // Description:  Return the number of elements in the list.
    this.Count = function()
    {
        return listArray.length;
    }
    //Method: Reverse
    //Description: Reverses the list
    this.Reverse = function () {
        listArray.reverse();
    }
    //Alias
    this.reverse = this.Reverse;
 
    // Method:  OrderBy
    // Description:  Order (ascending) the objects in the list by the given object property name.
    this.OrderBy = function (property/*, desc*/) {
        var r = copy(listArray.slice(0).sort(genericSort(property))),
        desc = arguments[1] || false;
        if (desc) r.reverse();            
        return r;
    }
 
    // Method:  OrderByDescending
    // Description:  Order (descending) the objects in the list by the given object property name.
    this.OrderByDescending = function (property) {
        return this.OrderBy(property, true);
    }
 
    // Method: Contains
    // Description: Determines if the given object is contained in the List
    this.Contains = function (object) {
        var contained = false;
        listArray.forEach(function (tEl) {
            Object.keys(object).forEach(function (key) {
                try {
                    if (tEl[key] === object[key]) contained = true;
                    else throw new Error();
                } catch (e) { contained = false; }
            });
        });
        return contained;
    }
 
    // Method: Distinct
    // Description: Gets a copy of the list with only unique elements.
    this.Distinct = function () {
        var results = new List();
        try {
            listArray.forEach(function (tEl) {
                if (!results.Contains(tEl)) {
                    results.Add(tEl);
                }
            });
        } catch (E) { }
        return results;
    }
 
    //Destructor
    this.Dispose = function () {
        List.instances[this.$key] = null;
        delete List.instances[this.$key];
 
        if (Object.keys(List.instances).length === 0) {
            List = null;
            delete List;
        };
 
        for (var p in this) {
            if (this.hasOwnProperty(p)) {
                if (this[p] instanceof Array) {
                    this[p].length = 0;
                };
                this.p = null;
                delete this.p;
            };
        };
    }
    this.dispose = this.Dispose;
 
    //Cleanup prototype
    for (var p in this) if (!this.hasOwnProperty(p)) delete this.p;
 
    //Add destructor
    window.addEventListener('unload', function () { this.Dispose(); } .bind(this));
 
    //If supported
    if (Object.freeze) Object.freeze(this);
 
    //Keep track
    var key = ++List.created;
    List.instances[key] = this;
}
 
// ===============  Static Methods  ======================================================
List.created = 0;
List.instances = {};
if (Object.freeze) Object.freeze(List);
 
//Method: indexOf
//Description adds logic to retrieve the index of an element from an array if present, otherwise -1
if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function (elt /*, from*/) {
        var len = this.length,
        from = Number(arguments[1]) || 0;
        from = (from < 0) ? Math.ceil(from) : Math.floor(from);
        if (from < 0) from += len;
        for (; from < len; from++)
            if (from in this && this[from] === elt) 
                return from;
        return -1;
    };
}
 
if (!Array.prototype.forEach) {
    Array.prototype.forEach = function () { };
}
 
if (!Object.prototype.keys) {
    Object.prototype.keys = function () { };
}
 
It will work in IE again with some minimal changes... I just got lazy.
 
Let me know what you think!
AnswerRe: I made some improvements PinmemberShawn Lawsure7-May-12 2:08 
GeneralRe: I made some improvements Pinmemberjfriedman7-May-12 2:58 
GeneralRe: I made some improvements PinmemberShawn Lawsure7-May-12 3:48 
GeneralRe: I made some improvements Pinmemberjfriedman7-May-12 6:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140926.1 | Last Updated 5 May 2012
Article Copyright 2012 by Shawn Lawsure
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid