1

I have a javascript array which defines an infinite tree like this.

z=['~GROUPHEAD 0~',
    'Intro',
    'Summary',
    ['~GROUPHEAD 1~',
        'Do this',
        ['~GROUPHEAD 2~',
            'Task 1',
            'Task 2'
        ]
    ]
]

Now
z[0]='~GROUPHEAD 0~';
z[1]='Intro';
z[2]='Summary';
z[3][0]='~GROUPHEAD 1~';
z[3][1]='Do this';
z[3][2][0]='~GROUPHEAD 2~';
z[3][2][1]='Task 1';
z[3][2][2]='Task 2';
z...[0] is a Header

Problem

I want to send a number say 322 or 3.2.2 or [3][2][2] to select 'Task 2' (i.e. z[3][2][2])
Can anyone help; also is there a better way to do this?

Edit

I need to call it 100s of time

Atul Gupta
  • 725
  • 1
  • 6
  • 20

6 Answers6

3

Here's one way: http://jsbin.com/uvenus (click the Edit in jsbin.com at the upper right to play with it). Here's the code:

Array.prototype.pick = function(num) {
  var levels = num.split('.');
  var out = this;
  for (var i=0;i<levels.length;i++) {
    out = out[parseInt(levels[i])];
  }
  return out;
};

// usage
myArray.pick('3.2.2');

So we create a new method on instances of Array called pick which accepts a string (3.2.2 doesn't make sense to JS as anything other than a string). We split that on the dots and then loop through the resulting array of numbers, applying each as the index to the original array (and the index is converted to an integer with parseInt() first, although JS will automatically typecast this for you if you left it out).

If you wanted this to be a little more foolproof it should have some error checking that the string you pass in only contains valid indexes and stuff like that, but this should get you started! :)

Rob Cameron
  • 9,674
  • 7
  • 39
  • 42
  • Thanks! Some people would freak out seeing me extend a core object in JS but it just feels better to use it that way, rather than passing the array to some other method. – Rob Cameron Sep 08 '11 at 22:02
2

you can either split the number and recursively look it up:

var path = '3.2.2',
    root = z;

var item = (function walk( root, path ){
    root = root[ path.shift() ];
    return path.length ? walk( root, path ) : root;
})( root, path.split('.') );

Or you can do something like:

var path = '3.2.2'
  , item = eval('z[' + path.replace(/[^\d\.]/g, '').split('.').join('][') + ']');

The line above:

  • sanitizes the string by removing non numbers/decimals
  • constructs a string "z[3][2][2]"
  • evaluates this new string to give you the result directly.

You should wrap this in a try/catch in case anyone passes an invalid path.

2

Solution without using eval that does not check for invalid path:

var z = ['~GROUPHEAD 0~',
    'Intro',
    'Summary',
    ['~GROUPHEAD 1~',
        'Do this',
        ['~GROUPHEAD 2~',
            'Task 1',
            'Task 2'
        ]
    ]
];

function accessArray(arr, index) {
    var parts = index.split(".");

    var a = arr;
    for(var i = 0; i < parts.length; i++)
        a = a[parts[i]];

    return a;
}

alert(accessArray(z, "3.2.2"));

http://jsfiddle.net/Xeon06/aHkW2/

Alex Turpin
  • 46,743
  • 23
  • 113
  • 145
1
function selectFromArray(ar, index)
{
  //get array from indexes
  var index_ar = index.split('.');
   if(index_ar.length > 1)
    {
     //set new index value
     var indx = "";
        //append new index value
        for(var i = 1; i < index_ar.length; i++)
        {              
          if(indx.length > 0)
             indx += '.';
           indx += index_ar[i];
         }
         //call method again with new index 
         return selectFromArray(ar[intParse(index_ar[0])], indx);
     }
    //return value
    return ar[intParse(index_ar[0])];  
}

i hope that my comments are describing functionality of this method, but it will do the trick

so you can call it like

selectFromArray(your_array, '3.4.3.2.1.2.3.2.2');

and it will fail if index out of bounds :)

Senad Meškin
  • 13,597
  • 4
  • 37
  • 55
1

Here's an Object Oriented way of doing the same thing.

MyCompanyGroup = function(name, description, task, parent, child) {
        if (arguments.length == 0) return;

        this._name = name;
        this._description = description;
        this._parent = parent;
        this._child = child;
}

MyCompanyGroup.prototype.toString =

function() {
        return "MyCompanyGroup";
}

var z = new MYCompanyGroup("~GROUPHEAD0~", "Intro", "Summary", this, this);

Now you can navigate up and down the hierarchy using parent child relations and you can also store all these objects in an array or in a hashmap for direct access.

In case you have an undefined number of fields, you can always embed an Array of name - value pairs inside MyCompanyGroup

There are two major advantages of this Object Oriented approach

  1. You can add more fields to this structure without breaking your code because array subscripts are not hard coded.
  2. You can access fields by name which makes the it easier to understand and maintain the code.
Community
  • 1
  • 1
RHT
  • 4,974
  • 3
  • 26
  • 32
0

Highly impractical but very fun to make :P

var num = 322;

if (num < 10)
  meh = z[num];
else if (num < 100)
  meh = z[Math.floor(num/10)][Math.floor(num % 10)];
else
  meh = z[Math.floor(num/100)][Math.floor(num/10 % 10)][Math.floor(num % 10)];

meh // results in "Task 2"
Joseph Marikle
  • 76,418
  • 17
  • 112
  • 129
  • Nice try, but what if there are mare than 10 fields in GROUPHEAD? say `3.12.2 -> 3122` – Atul Gupta Sep 08 '11 at 21:12
  • 1
    lol then it would fail :P but there are a myriad of ways it could go wrong. I just thought I'd throw an arithmetic reproach in the mix, but in reality eval is the best way imo. – Joseph Marikle Sep 08 '11 at 21:16