Cannot read Property '0' of Undefined, prototype incompatibility

Asked

Viewed 8,899 times

0

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

Cannot read property '0' of undefined

I have no idea how to solve because in fact the script is not even 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 playing the error https://jsfiddle.net/5h708bxj/1/

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

2 answers

7


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

To solve you have two alternatives, one being to use the method hasOwnProperty to check whether the item you are iterating on actually belongs to "items" and not to the "items" prototype, something similar to 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 extra iterations atoa, plus have an if. What I recommend is to use a traditional for or a foreach since both iterate strictly over 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, thefor(var k in letras[letra]), you have the same problem. Here comes a fiddle with your code working https://jsfiddle.net/5h708bxj/2/, however be sure to have understood what was the problem and why it was solved, otherwise you will end up falling into it again.

Note: a good practice is to use for. in only for objects, itere on arrays always with "traditional" or foreach’s Fors, plus much faster they will avoid you this kind of problem.

  • Thank you very much for the explanation and help!!

2

An alternative solution:

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

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>

See that I added the prototype.js.

The operated in is used for objects in general, as has already been said by Brundorb. And mainly, it does not capture the index but the element.

When you use:

for(item in itens)

item not 0, 1, 2, 3... but element 0, element 1...

In your case, you needed the index, with the forEach you have both options as arguments of the function itself: function(el, i){...

Note: I added "Zebra" for demo only.

  • I liked the use of the foreach I find it easier, thank you!!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.