¿Cómo diferenciar entre un objeto y un arreglo en JavaScript?

21

5

En ocasiones me encuentro que requiero validar si una variable tiene como estructura un arreglo u objeto, de tal forma pueda llevar una acción correspondiente dependiendo del tipo de valor almacenado a la variable.

¿Qué sería lo recomendable para distinguir entre un arreglo y un objeto?

Hablando de un objeto que no sea producto de una función instanciada.

if (isObject(valor)) {
    /* Código */
} else {
    /* Código */
}

Chofoteddy

Posted 2015-12-04T23:32:22.893

Reputation: 2 449

Answers

27

En JavaScript (ECMAScript 5) no solo hay objetos y arrays, hay en total 5 tipos primitivos:

string, number, undefined, boolean, object

La forma usual es usar el operador typeof que retornara la cadena que corresponda, es decir:

typeof 'hola' === 'string'
typeof true === 'boolean'
typeof 123 === 'number'
typeof undefined === 'undefined'
typeof {} === 'object'
typeof [] === 'object'

¿Cómo? Sí, los arrays son objetos regulares (donde typeof [] === "object") pero que tienen una relación entre la key (notación: valor[key]) y la propiedad length. Además heredan de Array.prototype.

Una buena forma (soportada por todos los navegadores actuales) de comprobar si un objeto es un array es: Array.isArray(valor)

Por lo tanto, no puedes simplemente diferenciar entre array y objeto, ya que existe la posibilidad de que no sea ni una cosa ni la otra.

Podrías hacer lo siguiente:

if (Array.isArray(valor)) {
  // es un array
} else if (typeof valor === 'object') {
  // es un objeto regular que no es un array
} else {
  // puede ser undefined, string, number o boolean.
}

¡Pero cuidado!, hay una excepcion, typeof null === 'object' así que también deberías validar si el valor es nulo o no, porque una variable con valor null daría un falso positivo como objeto. Esto es un bug de ECMAScript 5, la versión 6 lo corrige y retorna 'null'.

Quiero aclarar que la variable podría contener una función en cuyo caso seria typeof function(){} === 'function'

Espero que te sirva, saludos.

Más info en MDN, lamentablemente está en inglés.

Emanuel Ve

Posted 2015-12-04T23:32:22.893

Reputation: 13 498

1ES5 tiene 5 tipos primitivos: Undefined, Null, Boolean, Number y String. ES6 añade Symbol, primitivo también. El otro tipo es Object, que no es primitivo. En ES6 typeof null sigue devolviendo "object".Oriol 2015-12-16T23:17:17.093

@Oriol es cierto, lo escribí de memoria. cuando tenga algo de tiempo voy a editar el post e incluir la información actualizada. gracias por el mencionarloEmanuel Ve 2015-12-16T23:33:15.930

Hay una forma mejor que evita el problema del null, puedes revisar mi respuesta e incluirla en la tuya si deseasCarlos Muñoz 2015-12-05T05:23:15.013

10

Puedes usar Object.prototype.toString.call(valor) para conocer el tipo de objeto de valor

switch(Object.prototype.toString.call(valor)) {
    case '[object Array]':
        // Es un arreglo
        break;
    case '[object Object]':
        // Es un object
        break;
    default:
        // Es cualquier otro tipo incluyendo "Null", "Undefined",   
        // "Arguments", "Boolean", "Date", "Error", "Function", "JSON", "Math",
        // "Number", "RegExp" y "String"
}

Nota: La cadena [object class] donde class puede ser Undefined, Null o la clase del objeto es garantizada por la especificación de ECMAScript 5.1, sección 15.2.4.2. Los valores posibles para class pueden ser consultados en la sección 8.6.2

Carlos Muñoz

Posted 2015-12-04T23:32:22.893

Reputation: 9 341

@CarlosMuñoz Muy buena respuesta. Quizas te interese leer esto http://stackoverflow.com/q/36040671/4541024

devconcept 2016-03-16T17:15:36.827

@devconcept me preocupa el comentario de georg en esa pregunta. No estoy muy seguro a que se refiereCarlos Muñoz 2016-03-16T18:09:54.133

@CarlosMuñoz Se está refiriendo a la nota que está en la seccion 19.1.3.6 del spec de ES6, si te fijas en las correspondientes especificaciones de internal methods de ES5 y ES6 verás que el concepto de [[Class]] no está incluido sin embargo el resultado de los algoritmos no cambia; sino se perdería la compatibilidad ;)

devconcept 2016-03-16T18:34:26.493

3Fiarse de la representación del objeto que da toString es una muy mala idea. Nada te asegura que esto funcione en todos los entornos o que vaya a seguir funcionando en futuras versiones del lenguaje.Darkhogg 2015-12-07T13:40:25.020

@Darkhogg En otros lenguajes tal vez, en javascript no. Que funcione igual en todos los entornos me lo asegura la Especificación de ECMAScript en la sección 15.2.4.2 la propiedad interna [[Class]] puede ser revisada en la seccón 8.6.2 y efectivamente nada me asegura que siga funcionando en las futuras versiones del lenguaje, depende de que decida el comité de estándares como en cualquier otro lenguaje si es que desean de mantener la compatibilidad hacia atrás.

Carlos Muñoz 2015-12-07T15:23:33.177

@CarlosMuñoz El problema viene cuando empiezas a trabajar con instancias de clases definidas por el usuario, en cuyo caso cada cual puede definir el valor que quiera. Salvo que sobreescribamos dicho valor, este tipo de clases son indistinguibles de objetos normales, por lo que podemos empezar a tener problemas en seguida...Darkhogg 2015-12-07T15:40:35.680

La especificación no define ningun operador o forma de modificar la propiedad interna [[Class]], por favor revisa bien la sección 8.6.2 Las clases definidas por el usuario devolverán siempre [object Object]

Carlos Muñoz 2015-12-07T15:52:15.877

Nótese Object.prototype.toString se puede usar para conocer la "clase" de un valor, no su tipo. ES5 define la clase de un objeto como el valor de la propiedad interna [[Class]]. Este concepto desaparece en ES6, pero Object.prototype.toString continúa funcionando igual.Oriol 2015-12-16T23:23:06.597

6

¿Cómo diferenciar entre un objeto y un arreglo?

if( Object.prototype.toString.call( valor ) === '[object Array]' ) {
     alert( 'Arreglo!' );
} else if ( Object.prototype.toString.call( valor ) === '[object Object]' ) {
     alert( 'Objeto!' );
}

Jorgesys

Posted 2015-12-04T23:32:22.893

Reputation: 49 516

Fiarse de la representación del objeto que da toString es una muy mala idea. Nada te asegura que esto funcione en todos los entornos o que vaya a seguir funcionando en futuras versiones del lenguaje.Darkhogg 2015-12-07T13:40:43.307

@Darkhogg En otros lenguajes tal vez, en javascript no. Que funcione igual en todos los entornos me lo asegura la Especificación de ECMAScript en la sección 15.2.4.2 la propiedad interna [[Class]] puede ser revisada en la seccón 8.6.2 y efectivamente nada me asegura que siga funcionando en las futuras versiones del lenguaje, depende de que decida el comité de estándares como en cualquier otro lenguaje si es que desean de mantener la compatibilidad hacia atrás.

Carlos Muñoz 2015-12-07T15:34:50.983

No te preocupes, de hecho he cambiado mi respuesta, veo que el que pregunto realizo lo mismo. La pregunta correcta es ¿Cómo diferenciar entre un objeto y un arreglo?. Saludos.Jorgesys 2015-12-05T05:53:04.993

5

Un método sencillo sería aplicar la función JSON.stringify() a la variable y comprobar cuál es el primer carácter de la cadena resultante:

  • Si es un corchete ([), la variable era un array.
  • Si es una llave ({), la variable era un objeto.
  • En cualquier otro caso, la variable era algo diferente.

Teniendo eso en cuenta, he creado una función simple que devuelve la cadena "array" si el parámetro era un array, "objeto" si el parámetro era un objeto, u "otro" si es otro tipo de variable:

function arrayuobjeto(variable) {
  var aux = JSON.stringify(variable); 
  switch(aux[0]) {
    case "[": return "array";  break;
    case "{": return "objeto"; break;
    default:  return "otro";   break;
  }
}

Aquí dejo una serie de ejemplos:

function arrayuobjeto(variable) {
  //Si JSON.stringify lanza una excepción, entonces no era ni array ni objeto
  try {
    var aux = JSON.stringify(variable);
    switch(aux[0]) {
      case "[": return "array";  break;
      case "{": return "objeto"; break;
      default:  return "otro";   break;
    }
  } catch(ex) {
    return "otro";
  }
}
var ejemplos=[
  //Arrays
  [1,2] ,
  [] ,

  //Objetos
  {valor: 1} ,
  {} ,
  
  //Otros
  "[1,2]" ,
  "{ valor: 1}" ,
  "" ,
  " " ,
  1 ,
  0 ,
  true ,
  false ,
  null ,
  function(){} ,
  undefined
]
for(var i in ejemplos)
{
  console.log(
    arrayuobjeto(ejemplos[i]),
    typeof ejemplos[i]!="function"?
      JSON.stringify(ejemplos[i])
    :ejemplos[i]+""
  )
}

Alvaro Montoro

Posted 2015-12-04T23:32:22.893

Reputation: 30 630

3

La mejor forma para validar que un objeto es del tipo JSON o Array, recomiendo lo siguiente, tomando en cuenta que:

var a = [],
    j = {};

Solución 1

toString.call(o) === '[object Object]'; // true
toString.call(a) === '[object Array]'; // true

Solución 2

a.constructor.name === 'Array'; // true
o.constructor.name === 'Object'; // true

Pero estrictamente hablando, un arreglo es parte de la sintaxis de un JSON. Dicho esto los dos siguientes ejemplos son parte válida de dicha estructura.

Ejemplo 1:

console.log(response); // {"message": "success"}
console.log(response); // {"user": "bart", "id":3}

Ejemplo 2:

console.log(response); // [{"user":"chofoteddy"}, {"user":"bart"}]
console.log(response); // ["chofoteddy", "bart"]

En caso de contar con una variable de tipo texto (String) que desee validarse su estructura para conocer si coincide con alguno de ambos tipos mencionados, lo recomendable sería usar la siguiente función:

function isJSON (valor) {
    if (typeof valor !== 'string')
        valor = JSON.stringify(valor);

    try {
        JSON.parse(valor);
        return true;
    } catch (e) {
        return false;
    }
}

Chofoteddy

Posted 2015-12-04T23:32:22.893

Reputation: 2 449

3isJSON(null), isJSON(1) y isJSON(true) devuelven true, que no queda en la definición de "tiene como estructura un arreglo u objeto"Carlangueitor 2015-12-05T00:10:50.147

1No solo no son ni arreglos ni objetos sino que ni siquiera son JSON válidoCarlos Muñoz 2015-12-05T00:30:01.293

2

Podrias usar para detectar json un

try{
    JSON.parse(valor);
    tipo = "JSON";
 }catch (Exception e){
    tipo =  typeof(valor) 
    //retornara undefined, number, boolean, string, object,null,function

  }

Puedes ver mas info del typeof aca

jpganz18

Posted 2015-12-04T23:32:22.893

Reputation: 430

typeof no es una funciónCarlangueitor 2015-12-05T00:17:08.777

pero la puedes invocar asi... la puedes igualar a una variable para leer despues...jpganz18 2015-12-05T00:19:07.690

1

Hace tiempo tuve un problema similar, y me encontré con la siguiente solución, en donde type se le asigna array u object dependiendo que tipo sea tu json, en este ejemplo es un objeto.

var myJson = {"name":"juan"};

var type = Object.prototype.toString.call(myJson).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();

if (type == "array") {                 

} else if (type == "object") {

}

espero que te funcione.

José Gregorio Calderón

Posted 2015-12-04T23:32:22.893

Reputation: 856

1

Respuesta corta

Si tu objetivo son navegadores modernos, usa Array.isArray(variable) el cual fue introducido en ECMAScript 5. Todos los detalles en Array.isArray().

Explicación

De acuerdo a la ECMAScript 7, un arreglo, también llamado matriz, es un objeto exótico que da un tratamiento especial a las llaves de propiedades de indice del arreglo/matriz.

En otras palabras, un arreglo comparte es un objeto con ciertas particularidades lo cual hacía complicado distinguir entre un arreglo y un objeto que no es un arreglo. Afortunadamente Array.isArray() fue introducido como una función estándar y es soportado por los navegadores modernos.

Demostración

/* Primitivos */
console.info(Array.isArray(true)); // Booleano true. Devuelve false
console.info(Array.isArray(1)); // Entero. Devuelve false
console.info(Array.isArray(3.14)); // Número de punto flotante. Devuelve false
console.info(Array.isArray('Hola')); // Cadena. Devuelve false
console.info(Array.isArray(null)); // null. Devuelve false
console.info(Array.isArray(undefined)); // undefined. Devuelve false
console.info(Array.isArray(Symbol())); // Symbol. Devuelve false

/* Objetos */
console.info(Array.isArray({nombre:"Benito",apellido:"Juárez"})); // Objeto literal. Devuelve false
console.info(Array.isArray([1,2,3])); // Matriz. Devuelve true

Sin embargo, si ocupas una solución mas general, en que se identifique el tipo de dato, considera usar toString.call() ya que este devolverá una cadena de la forma [object class] donde class es indica el tipo de dato del que se trata.

Nota: No asumir que debido a que se indica object para los primitivos, todo es un objeto en JavaScript ya que esto es incorrecto. Lo que pasa es que JavaScript convierte temporalmente el primitivo a un objeto para poder llamar propiedades del objeto correspondiente, en este caso la función toString

function describeTipo(o){
   console.info(toString.call(o));
}

describeTipo(1); // Devuelve [object Number]
describeTipo(3.14); // Devuelve [object Number]
describeTipo('Hola'); // Devuelve [object String]
describeTipo(null); // Devuelve [object Null]
describeTipo(undefined); // Devuelve [object Undefined]
describeTipo(Symbol()); // Devuelve [object Symbol]
describeTipo({nombre:"Benito",apellido:"Juárez"});// Devuelve [object Object]
describeTipo([1,2,3]); // Devuelve [object Array]

Rubén

Posted 2015-12-04T23:32:22.893

Reputation: 4 965