Can not read property '0' of undefined, incompatibility with prototype

0

I'm using a script that sorts an JSON in alphabetical order with an indicative header, as shown in question however I am not able to make it work in harmony with prototype, I get the error:

Cannot read property '0' of undefined

I have no idea how to solve it because in fact the script is not mine, so I'm lost, I wanted to solve it. Here's the script:

var itens = JSON.parse('[{"user_id":"3333","username":"cacau"},{"user_id":"3333","username":"balmer"}]');

itens.sort(function(a,b){ 
    return a.username.localeCompare(b.username);
});

var letras = {};

for(var i in itens) {
    console.log(itens);
    var l = itens[i].username[0].toUpperCase();
    letras[l] = letras[l] || [];
    letras[l].push(itens[i]);
}

for(var letra in letras) {
  document.write('LETRA ' + letra + "<br />");

  for(var k in letras[letra]) {
    document.write(letras[letra][k].username + "<br />");
  }

  document.write('<br />');
}

A fiddle reproducing the error link

Note: lib prototype.js is being used along with this code.

    
asked by anonymous 18.01.2016 / 01:51

2 answers

6

The bug occurs because you are using prototype.js lib, which extends the native javascript array object by including a number of new methods in it, such as you iterate over array "items" with notation for..in in addition to passing through the two parsed elements of your JSON it will also include all methods contained in the prototyping chain of your array object.

To solve you have two alternatives, one being to use the hasOwnProperty to check if the item you're iterating over really belongs to "items" and not to the prototype of "items", something like this:

for(var i in itens) { // itera sobre os dois elementos do array itens + uma porrada de métodos do prototype de "array"
     if (itens.hasOwnProperty(i)) {
        var l = itens[i].username[0].toUpperCase();
        ...

This solution above is crude since you will do several iterations atoa in addition to having an if. What I recommend is to use a traditional forEach or a forEach since both have strictly focused on the array elements, ignoring the prototype methods that are the cause of the problem. Example:

for (var i=0; i<itens.length; i++) { // itera apenas sobre os elementos do array
    var l = itens[i].username[0].toUpperCase();

Note that in your third for, for(var k in letras[letra] ), you have the same problem. Here's a fiddle with your code working link , however make sure you understand what the problem was and why it was resolved, if it otherwise you will end up falling on it again.

Note: a good practice is to use for..in only for objects, iterate over arrays always with "traditional" or forEach's fors, in addition to much faster they will avoid this type of problem .

    
18.01.2016 / 02:28
2

A Workaround:

As itens is an array of objects I used a forEach , and I captured the index. I did the same in letras[letra] , since k was an attempt to capture the index too:

var itens = JSON.parse('[{"user_id":"3333","username":"cacau"},{"user_id":"3333","username":"balmer"},{"user_id":"3333","username":"zebra"}]');

itens.sort(function(a,b){ 
    return a.username.localeCompare(b.username);
});

var letras = {};

itens.forEach(function(el, i){
    console.log(itens);
    var l = itens[i].username[0].toUpperCase();
    letras[l] = letras[l] || [];
    letras[l].push(itens[i]); // usa o índice para selecionar um objeto de "itens"..
})

for(var letra in letras) {
  document.write('LETRA ' + letra + "<br />");
  letras[letra].forEach(function(el, i){
    document.write(letras[letra][i].username + "<br />"); // Usa o "i" (índice) ao invés do "k" (elemento)
  }) 
  document.write('<br />');
}
<script src="https://ajax.googleapis.com/ajax/libs/prototype/1.7.2.0/prototype.js"></script>

SeethatI'veaddedprototype.js.

Theoperandinisusedforobjectsingeneral,ashasalreadybeensaidbyBrundoRB.Andmostimportantly,itdoesnotcapturetheindexbuttheelement.

Whenyouuse:

for(iteminitens)

itemisnot0,1,2,3...andyeselement0,element1...

Inyourcaseyouneededtheindex,withforEachyouhavebothoptionsasargumentsofthefunctionitself:function(el,i){...

Note:Iadded"Zebra" just for demonstration.

    
18.01.2016 / 02:38