2

I have this object:

function formBuddy()
{
    var fields = new Array();
    var labels = new Array();
    var rules = new Array();
    var count=0;

    this.addField = function(field, label, rule)
    {
        fields[count] = field;
        labels[field] = label;
        rules[field] = rule;
        count = ++count;
    }
}

Its used in this way:

var cForm=new formBuddy();
cForm.addField("c_first_name","First Name","required");
cForm.addField("c_last_name","Last Name","required");

The problem is, in the addField() function the fields array is being set correct (perhaps because a numerical index is being used to refer to it) but the other 2 arrays (labels and rules) aren't being touched at all. Doing a console.log shows them as empty in firebug.

What do I need to change to make them work? I'd still like to refer to the rules and labels by the string index of the field.

Gumbo
  • 643,351
  • 109
  • 780
  • 844
Ali
  • 261,656
  • 265
  • 575
  • 769
  • Javascript don't have native associative arrays, only objects. Objects have properties and the name of the property is always a string. Even the index of arrays is converted to a string before the 'array magic' happens. – some Feb 22 '09 at 20:13

4 Answers4

8

Use objects instead:

function formBuddy()
{
    var fields = {};
    var labels = {};
    var rules = {};
    var count = 0;

    this.addField = function(field, label, rule)
    {
        fields[count] = field;
        labels[field] = label;
        rules[field] = rule;
        count++;
    }
}

But as Christoph already mentioned, I would store this information in a single data structure too. For example:

function formBuddy() {
    var fields = {};
    this.addField = function(name, label, rule) {
        fields[name] = {
            name: name,
            label: label,
            rule: rule
        };
    };
    this.getField = function(name) {
        return fields[name];
    };
}

var cForm=new formBuddy();
cForm.addField("c_first_name","First Name","required");
cForm.addField("c_last_name","Last Name","required");
alert(cForm.getField("c_last_name").label);
Community
  • 1
  • 1
Gumbo
  • 643,351
  • 109
  • 780
  • 844
  • Indeed, javascript has no associative arrays. An associative array is an object. – Pim Jager Feb 22 '09 at 19:11
  • If I did rules[field] then (where field is a string containing a field name) would it work? – Ali Feb 22 '09 at 19:14
  • Why use Objects when you can simply augment Arrays? -1. – Luca Matteis Feb 22 '09 at 19:25
  • Because he probably intended for "labels" and "rules" to be looked-up by field name instead of index? – chakrit Feb 22 '09 at 19:29
  • @Luca: because it's bad practice to mis-use arrays as maps? `fields` should be an array because the keys are numeric, `labels` and `rules` should be objects if you want to use non-numeric keys; for another solution, check my answer below – Christoph Feb 22 '09 at 19:30
  • @Christoph: that's right, but sometimes you need represent objects in arrays, and given the example of the question, it seems perfectly suitable. @chakrit: They are just normal objects, you can look them up by field name. – Luca Matteis Feb 22 '09 at 19:34
  • @Luca - the data is a related set of >1 fields. That's clearly an object, and storing as such allows reuse, a robust association and the possibility of prototyping. There's a reason OO exists and we aren't all still writing C. – annakata Feb 22 '09 at 21:32
  • @annakata, why do you and other people seem to not understand that an Array is an object? – Luca Matteis Feb 22 '09 at 22:29
  • @Luca: annakata's comment referred to grouping `label` and `rule` together in a single object instead of storing them separately in different arrays – Christoph Feb 22 '09 at 22:45
  • @Luca: the problem with using arrays as stores for arbitrarily named properties is that names like `splice`, `push`, ... will shadow the methods with these names; also, if you don't use any array-specific features, why should you use an array instead of a plain objects? – Christoph Feb 22 '09 at 22:49
2

fields should be an array, whereas labels and rules should be objects as you want to use strings as keys. Also, addField() is the same for each instance of FormBuddy() (names of constructor functions should be capitalized) and should reside in the prototype, ie

function FormBuddy() {
    this.fields = []; // this is the same as `new Array()`
    this.labels = {}; // this is the same as `new Object()`
    this.rules = {};
}

FormBuddy.prototype.addField = function(field, label, rule) {
    this.fields.push(field);
    this.labels[field] = label;
    this.rules[field] = rule;
};

You can access the labels/rules via

var buddy = new FormBuddy();
buddy.addField('foo', 'bar', 'baz');
alert(buddy.labels['foo']);
alert(buddy.rules.foo);

Just to further enrage Luca ;), here's another version which also dosn't encapsulate anything:

function FormBuddy() {
    this.fields = [];
}

FormBuddy.prototype.addField = function(id, label, rule) {
    var field = {
        id : id,
        label : label,
        rule : rule
    };

    this.fields.push(field);
    this['field ' + id] = field;
};

FormBuddy.prototype.getField = function(id) {
    return this['field ' + id];
};

var buddy = new FormBuddy();
buddy.addField('foo', 'label for foo', 'rule for foo');

It's similar to Gumbo's second version, but his fields object is merged into the FormBuddy instance. An array called fields is added instead to allow for fast iteration.

To access a field's label, rule, or id, use

buddy.getField('foo').label

To iterate over the fields, use

// list rules:
for(var i = 0, len = buddy.fields.length; i < len; ++i)
    document.writeln(buddy.fields[i].rule);
Christoph
  • 164,997
  • 36
  • 182
  • 240
  • I think he/she wants the properties to be private, I could be wrong though. – Luca Matteis Feb 22 '09 at 19:47
  • @Luca: I never understood why so many people are fond of these so-called 'private' variables: it's inefficient - for each instance, an additional function object holding a reference to the closure over the constructor function's variable object has to be created - and imo not 'in the spirit' of JS – Christoph Feb 22 '09 at 20:25
  • @Christoph: you clearly never developed large enough applications in an object oriented environment to not be able to understand `privatization`. – Luca Matteis Feb 22 '09 at 20:30
  • @Luca: I'm quite aware of encapsulation and information hiding, but also of the concepts called over-engineering and efficiency; in JavaScript, I'd prefer exposing properties directly over implementing unnecessary getters any day – Christoph Feb 22 '09 at 20:36
  • Private in JS is ridiculous - it's just not required in the way it is in build-compiled languages and wastes another few bytes of download time. Definite over-engineering 99% of the time. FWIW, I think the 'field'+ is kind of pointless, I'd just go with id alone – annakata Feb 22 '09 at 21:38
  • @annakata: dropping the prefix would enable us to get rid of `getField()`, but it might lead to naming conflicts - what about fields with ids like 'addField'? It's either using a different object as store or adding a namespace prefix; I went with the second one to add confusion to the code ;) – Christoph Feb 22 '09 at 21:49
  • btw, if the ids are actual HTML ids, it might be a good idea to use '#' as prefix... – Christoph Feb 22 '09 at 22:20
0

Arrays are treated as Objects in Javascript, therefore your piece of code works, it's just that firebug's console.log isn't showing you the "Objects" inside the array, rather just the array values ...

Use the for(var i in obj) to see what objects values the Array contains:

function formBuddy() {
    var fields = new Array();
    var labels = new Array();
    var rules = new Array();
    var count=0;

    this.addField = function(field, label, rule)
    {        
        fields[count] = field;
        labels[field] = label;
        rules[field] = rule;
        count = ++count;

        for(var i in labels) {
            console.log(labels[i]);
        }
        for(var i in rules) {
            console.log(rules[i]);
        }

        console.log(labels.c_last_name);
        // or
        console.log(labels["c_last_name"]);
    }
}

var cForm = new formBuddy();
cForm.addField("c_last_name","Last Name","required");
Luca Matteis
  • 29,161
  • 19
  • 114
  • 169
  • What i need to do is to be able to retreive the rule of a field. E.g rule=rules[field], or rule=rules.field. That isn't currently working with the arrays code and gives me an error. – Ali Feb 22 '09 at 19:28
  • Works for me console.log(labels["c_last_name"]); or console.log(labels.c_last_name); – Luca Matteis Feb 22 '09 at 19:30
  • you should check `hasOwnProperty()` when looping over the properties - some time ago, it was fashionable to extend `Array.prototype` – Christoph Feb 22 '09 at 21:33
0

Is this an alternative to what you are seeking ? Try it.

<script type="text/javascript">"use strict";
  function formBuddy() {
      this.fields = new Array();
      this.labels = new Array();
      this.rules = new Array();
      this.count=0;

      this.addField = function(field, label, rule)
      {
          this.fields[this.count] = field;
          this.labels[field] = label;
          this.rules[field] = rule;
          this.count++;
      }
  }

  var cForm = new formBuddy();

  // FILLING IN THE DATABASE
  cForm.addField("c_first_name","Diasoluka","duplicated");
  cForm.addField("c_Middle_name","Nz","inspect");
  cForm.addField("c_last_name","Luyalu","mandatory");
  cForm.addField("c_first_name","Diasoluka","duplicated");


  console.log(`ACCESSING EACH PROPERTY INDIVIDUALLY\n,${'.'.repeat(31)}`);

  let el=Object.entries(cForm.fields); // DESTRUCTURING
      console.log(Object.is(el,Object.entries(cForm.fields)));
      // false

  const [f1,f2,f3] = el; // DESTRUCTURING=DÉCOMPOSITION
  console.log("FIELDS:\n",f1," | ",f2," | ",f3,"\n\n");
  // FIELDS:
    // Array [ "0", "c_first_name" ]
    // Array [ "1", "c_Middle_name" ]
    // Array [ "2", "c_last_name" ]

  let labels = Object.entries(cForm.labels); // DESTRUCTURING
  const [l1,l2,l3] = labels; // DESTRUCTURING
  console.log("LABELS:\n",l1," | ",l2," | ",l3,"\n\n");
  // LABELS:
    // Array [ "c_first_name", "Diasoluka" ]
    // Array [ "c_Middle_name", "Nz" ]
    // Array [ "c_last_name", "Luyalu" ]

  let rules = Object.entries(cForm.rules); // DESTRUCTURING
  const [r1,r2,r3] = rules; // DESTRUCTURING
  console.log("RULES:\n",r1," | ",r2," | ",r3,"\n\n");
  // RULES:
    // Array [ "c_first_name", "duplicated" ]
    // Array [ "c_Middle_name", "inspect" ]
    // Array [ "c_last_name", "mandatory" ]


  console.log(`PAESING THE DATABASE =\nACCESSING ALL THE FIELDS AT ONCE\n,${'.'.repeat(31)}`);

  for(let key in cForm.fields){
    let el=cForm.fields[key]; // ASSIGNMENT=AFFECTATION
        console.log(Object.is(el,cForm.fields[key]));
        // true true true true

    console.log(`${el} // ${cForm.labels[el]} // ${cForm.rules[el]}\n`);
  }
  // c_first_name // Diasoluka // duplicated
  // c_Middle_name // Nz // inspect
  // c_last_name // Luyalu // mandatory
  // c_first_name // Diasoluka // duplicated


  console.log("\n");


  console.log(`THE INNER STRUCTURE OF OUR cForm OBJECT\n,${'.'.repeat(31)}`);
  console.log(Object.entries(cForm)); // DESTRUCTURING
  // (5) [Array(2), Array(2), Array(2), Array(2), Array(2)]
    // 0: (2) ['fields', Array(4)]
    // 1: (2) ['labels', Array(0)]
    // 2: (2) ['rules', Array(0)]
    // 3: (2) ['count', 4]
    // 4: (2) ['addField', ƒ]
    // length: 5
    // [[Prototype]]: Array(0)
</script>