A bit about pseudo-arrays (array-like objects or collections). “What is this?”, “How to work with them?” And so on

    In this article I will talk about pseudo-arrays: “What is this?”, “How to work with them?”, “How do they differ from an array?”, “How to convert them to an array?”.

    Also, before starting, I’ll clarify that this information applies only to JavaScript. That is, I will talk about pseudo-arrays in the JavaScript language.

    What is a pseudo-array (array-like object or collection)?


    A pseudo-array is an object that is structurally similar to an array. That is, it has numerical properties (indices) and a property length.

    Example:
    {0: 'Значение 1', 1: 'Значение 2', 2: 'Значение 3', length: 3};

    How is a pseudo-array different from an array?


    The main difference between a pseudo-array and an array is its prototype inheritance, that is, a property __proto__.

    When we look at the properties of an array, we will see that it inherits the prototype of the Array object. That is, all the properties that are in the Array.prototype object will be available for any array. If you look at the properties of a pseudo-array, you can see that it inherits the prototype of another object along with other properties.

    List of object types that are related to pseudo-arrays


    A pseudo-array can inherit prototypes of various objects. Here is a small list of object types - pseudo-arrays:

    • DOMTokenList
    • NamedNodeMap
    • DOMStringMap
    • HTMLCollection
    • NodeList
    • HTMLAllCollection
    • StyleSheetList
    • DOMStringList
    • HTMLMapElement
    • CSSRuleList

    This is a small part of the complete list of pseudo-array types, which has more than fifty types. The next paragraph of the article follows from this.

    How to distinguish a normal object from a pseudo-array?


    For three days I sorted this question out while reading various articles and as a result I made only one condition: if an object is a pseudo-array, then it must have a property lengththat must be an integer and be greater than or equal to zero.

    Number.isInteger(Number(object.length)) && Number(object.length) >= 0

    I made this condition by folding the following items:

    1. You can not be equal to the numerical properties, because if you do not specify them, this does not mean that they will not be. They will simply be equal to the valueundefined
    2. When I looked at the types of pseudo-arrays, I saw that their types contained the word Collection, Mapeither List. But this idea is dispelled immediately, as psevdomassiv may have a type of conventional object - Object,
      and in general it's silly, because at this point, even the usual array will not fall.
    3. It is also impossible to equal non-numeric properties, because a non-numeric property can be in an array.

    But JavaScript "said" that my condition is too cruel. When I analyzed the options for converting a pseudo-array into an array, I realized that JavaScript would “eat” a pseudo-array in which the property lengthis equal to a number that is greater than or equal to zero.

    typeof object.length === 'number' && Number(object.length) >= 0

    And it is not necessary that the number be an integer (except in some cases). JavaScript will simply translate the fractional number into the largest integer less than or equal to the specified one.

    Example:
    Array.from({0: 'Значение 1', 1: 'Значение 2', length: 1.6}); // ['Значение 1']
    Array.from({0: 'Значение 1', 1: 'Значение 2', 2: 'Значение 3', length: 2.3}); // ['Значение 1', 'Значение 2']
    

    How to convert a pseudo-array to an array?


    To convert a pseudo-array to an array, there are several options:

    1. Enumerate the values ​​of the pseudo-array into a regular array
      The first option that comes to the minds of beginners is to loop through all the values ​​from the pseudo-array to the array.

      var object = {0: 1, 1: 2, 2: 3, length: 3}
      var array = [];
      // Преобразуем псевдомассив в массив
      for (var i = 0; i < object.length; i++) {
         array.push(object[i]);
      };
      console.log( array ); // [1, 2, 3]

    2. Using the functionArray.from()
      This option is a bit controversial, since the tables that browsers support this function on different sites are different. But I can confidently say that in all modern browsers this method will work.

      var object = {0: 1, 1: 2, 2: 3, length: 3}
      // Преобразуем псевдомассив в массив
      var array = Array.from(object);
      console.log( array ); // [1, 2, 3]

    3. Using the function Array.prototype.slice.call()( [].slice.call())
      This method of "our grandfathers and grandmothers", which still works.

      var object = {0: 1, 1: 2, 2: 3, length: 3}
      // Преобразуем псевдомассив в массив
      var array = Array.prototype.slice.call(object); // Или сокращённая форма: [].slice.call(object);
      console.log( array ); // [1, 2, 3]

    4. Using the spread operator
      This method is quite controversial at the time of writing this article, as it is still not supported by all browsers and will only work with “root” pseudo-arrays ( NodeList, HTMLCollectionand so on).

      var object = document.querySelectorAll(selector);
      // Преобразуем псевдомассив в массив
      var array = [...object];
      console.log( array ); // [element, element, element]

    5. By changing the__proto__
      Pro property, I mentioned this property at the beginning of the article. If we change the property of the __proto__object to Array.prototype, then the pseudo-array is converted to an array. But this method is included in those “except for some cases” that I wrote about, because, for complete conversion to an array, the property lengthmust be an integer.

      var object = {0: 'a', 1: 'b', 2: 'c', length: 3}
      // Меняем __proto__ объекта
      object.__proto__ = Array.prototype;
      console.log(object); // ['a', 'b', 'c']

      There is also one feature here: if we specify a lengthnumber that will be less than the number of records in the pseudo-array, then we get an array with the number of records specified in lengthand with additional properties from the rest of the records.

      var object = {0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', length: 3}
      // Меняем __proto__ объекта
      object.__proto__ = Array.prototype;
      console.log(object); // ['a', 'b', 'c', 3: 'd', 4: 'e']

      And one more note: this method will not make the object a real array, although it will give it the necessary parameters. You can verify this by checking the object using the function Array.isArray();.

      var object = {0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', length: 3}
      // Меняем __proto__ объекта
      object.__proto__ = Array.prototype;
      console.log( Array.isArray(object) ); // false


    These are the most popular transformation methods. It must also be said that all these methods can not be used if you need, for example, to iterate over a pseudo-array with forEachor filter it with a function filter. For such purposes, functions have an additional function .call()that makes it possible to work with pseudo-arrays.

    Example:
    var object = {0: 'a', 1: 'b', 2: 'c', length: 3}
    // Создаём массив из значений псевдомассива
    object = Array.prototype.map.call(object, v => 'Буква: ' + v); // Или сокращённо: [].map.call(object, v => 'Буква: ' + v)
    console.log(object); // ['Буква: a', 'Буква: b', 'Буква: c']

    On this I will end this article. He told me what he wanted, and whether it was useful for you is up to you.

    Only registered users can participate in the survey. Please come in.

    What method do you use to convert a pseudo-array to an array?

    • 18.6% I iterate over the values ​​of the pseudo-array into a regular array 20
    • 28.9% I use the function Array.from () 31
    • 34.5% I use the function Array.prototype.slice.call () 37
    • 19.6% I use spread operator 21
    • 0.9% I am changing the property __proto__ 1
    • 4.6% I use several options to provide cross-browser compatibility 5
    • 6.5% None. I use additional functions Array.prototype. [{Function}]. Call () 7
    • 20.5% I don't use anything at all 22

    Also popular now: