Como verificar se um objeto é uma matriz?

2753

Estou tentando escrever uma função que aceita uma lista de seqüências de caracteres ou uma única seqüência de caracteres. Se for uma string, quero convertê-la em uma matriz com apenas um item, para que eu possa fazer um loop sobre ela sem medo de erro.

Então, como verifico se a variável é uma matriz?


Reuni as várias soluções abaixo e criei um teste jsperf . Eles são todos rápidos, portanto, basta usar Array.isArray- ele é bem suportado agora e funciona em vários quadros .

mpen
fonte
6
Eu pensei que você queria 'verificar se o objeto é uma matriz', mas você deseja verificar se 'objeto é uma matriz de seqüências de caracteres ou uma única sequência' especificamente. Não tem certeza se você vê? Ou é só comigo? Eu estava pensando em algo mais como este ... sou eu quem está perdendo alguma coisa aqui?
Rd1g0
149
TL; DR - arr.constructor === Arrayé o mais rápido.
Neta
3
jsben.ch/#/QgYAV - uma referência para as formas mais comuns
EscapeNetscape
39
TL; DR - matriz. isArray (arr) desde ES5; e $. isArray (arr) no jQuery.
Ondra Žižka
6
Lembre-se de que, se por algum motivo você sobrescrever seu construtor via protótipo, esse arr.constructor === Arrayteste retornará falso. Array.isArray(arr)ainda retorna verdadeiro embora.
Ghaschel

Respostas:

979

Nos navegadores modernos, você pode fazer

Array.isArray(obj)

( Suportado pelo Chrome 5, Firefox 4.0, IE 9, Opera 10.5 e Safari 5)

Para compatibilidade com versões anteriores, você pode adicionar o seguinte

# only implement if no native implementation is available
if (typeof Array.isArray === 'undefined') {
  Array.isArray = function(obj) {
    return Object.prototype.toString.call(obj) === '[object Array]';
  }
};

Se você usa jQuery, pode usar jQuery.isArray(obj)ou $.isArray(obj). Se você usar sublinhado, poderá usar_.isArray(obj)

Se você não precisa detectar matrizes criadas em quadros diferentes, também pode usar instanceof

obj instanceof Array
Fela Winkelmolen
fonte
9
Aqui está uma mais completa lista de navegadores que suportamArray.isArray
lightswitch05
if (typeof Array.isArray === 'undefined') {pode ser alterado paraif(!Array.isArray) {
iMatoria 21/05
Pois o que vale a pena Object.prototype.string.call(obj)pode ser falsificado se o objeto Symbol.toStringTagestiver nele. Dito isto, não conheço nenhum ambiente que seja enviado, Symbol.toStringTagmas não, Array.isArrayportanto isso parece seguro.
Benjamin Gruenbaum
5
Por que instanceof Arrayfalha se a matriz é de um quadro diferente?
NobleUplift
16
@NobleUplift: instanceof Arrayfalha se a matriz for de um quadro diferente porque cada matriz desse quadro diferente possui um Arrayconstrutor e um protótipo diferentes . Por motivos de compatibilidade / segurança, todos os quadros têm seu próprio ambiente global e isso inclui objetos globais. O Objectglobal de um quadro é diferente do Objectglobal de outro. O mesmo Arrayvale para os globais. Axel Rauschmayer fala mais sobre isso .
jschoi
1943

O método fornecido no padrão ECMAScript para encontrar a classe de Object é usar o toStringmétodo de Object.prototype.

if( Object.prototype.toString.call( someVar ) === '[object Array]' ) {
    alert( 'Array!' );
}

Ou você pode usar typeofpara testar se é uma String:

if( typeof someVar === 'string' ) {
    someVar = [ someVar ];
}

Ou, se você não está preocupado com o desempenho, basta fazer um concatpara uma nova matriz vazia.

someVar = [].concat( someVar );

Há também o construtor que você pode consultar diretamente:

if (somevar.constructor.name == "Array") {
    // do something
}

Confira um tratamento completo no blog de @TJ Crowder , conforme publicado em seu comentário abaixo.

Confira este benchmark para ter uma idéia de qual método tem melhor desempenho: http://jsben.ch/#/QgYAV

De @Bharath, converta string em array usando Es6 para a pergunta:

const convertStringToArray = (object) => {
   return (typeof object === 'string') ? Array(object) : object 
}

suponha:

let m = 'bla'
let n = ['bla','Meow']
let y = convertStringToArray(m)
let z = convertStringToArray(n)
console.log('check y: '+JSON.stringify(y)) . // check y: ['bla']
console.log('check y: '+JSON.stringify(z)) . // check y: ['bla','Meow']
user113716
fonte
65
+1 Sim, toStringé um dos caminhos a seguir. Eu faço um pouco de um resumo aqui: blog.niftysnippets.org/2010/09/say-what.html
TJ Crowder
3
typeof new String('beans') > 'objeto'
Ben
16
Se você não quiser digitar "[array de objetos]", use Object.prototype.toString.call (someVar) === Object.prototype.toString.call ([]) ou crie uma função de conveniência para obter o tipo, se você não 't deseja digitar Object.prototype.toString.call
Pramod
13
Eu uso o vanilla Array.isArray, que funciona em 'navegadores modernos' (ou seja, IE9 + e todos os outros). E, para suporte a navegadores antigos, use o shim do MDN developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/…
David Gilbertson
21
Vive no mundo moderno - #Array.isArray(obj)
123515 mcfedr
1274

Primeiro, verificaria se sua implementação suporta isArray:

if (Array.isArray)
    return Array.isArray(v);

Você também pode tentar usar o instanceofoperador

v instanceof Array
ChaosPandion
fonte
127
v instanceof Arrayretornará false se vfoi criado em outro quadro ( vé instância da thatFrame.contentWindow.Arrayclasse).
precisa saber é
47
Para ser específico: Array.isArrayé definido como parte do ECMAScript 5 / Javascript 1.8.5.
Jevon
8
Solução realmente simples e limpa, MAS o isArray não é compatível com alguns navegadores mais antigos (ex IE7 e IE8). Fonte: kangax.github.io/es5-compat-table/#
Wookie88
2
Que tal: if (Array.isArray) retornar Array.isArray (v); else return v instância de Array;
Lewdev
1
ou apenas:return (Array.isArray && Array.isArray(v)) || (v instanceof Array);
Stijn de Witt
298

O jQuery também oferece um $.isArray()método:

var a = ["A", "AA", "AAA"];

if($.isArray(a)) {
  alert("a is an array!");
} else {
  alert("a is not an array!");
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

janr
fonte
27
Apenas uma nota, jQuery usa o método toString internamente: GitHub Fonte
Jacob Squires
5
@JacobSquires depende. Acabei de testar aqui, o jQuery mais recente no Chrome - $.isArray === Array.isArrayestá retornando verdadeiro.
Renan
3
@ Renan: Esta é a coisa boa sobre o uso do jQuery. Ele geralmente usa o método mais moderno e melhor para fazê-lo, e você não precisa fazer todo o recurso verificando a si mesmo para saber o que usar.
temor
jQuery está usando Array.isArraynos bastidores: github.com/jquery/jquery/blob/master/src/core.js#L211
Sergiu
1
Mas ninguém vai usar o jQuery APENAS PARA essa funcionalidade, certo? Eu não apenas baixaria o jquery porque quero verificar se algo é uma matriz: p
Automagisch
106

Este é o mais rápido entre todos os métodos (todos os navegadores suportados):

function isArray(obj){
    return !!obj && obj.constructor === Array;
}
Shinobi
fonte
2
Você está certo, esse é o mais rápido de acordo com os testes que incluí na pergunta.
MPEN
5
Existem desvantagens em usar esse método? Parece muito mais simples e eficaz do que a resposta top aceita.
David Meza
@ shinobi - apenas curioso (e eu já vi isso frequentemente) - por que você expressa a condição if (obj && Array === obj.constructor)em vez de if (obj && obj.constructor === Array)? Está perdido na tradução para o inglês e depois no código? por exemplo, os falantes de inglês geralmente tendem a perguntar "o objeto existe e seu construtor vem da classe da matriz?", portanto o código flui ao lê-lo é mais lógico. ou existe algum motivo técnico?
não sincronizado
function object_type(o){var t = typeof(o);return ((t==="object") && (o.constructor===Array)) ? "array" : t;} /*allows you to */ switch(object_type(o)){ case 'array': break; case 'object' : o.dosomething();}
não sincronizado
3
@shinobi tudo de bom. Suponho que possa ser uma ressaca de um hábito c seguro - se você usar acidentalmente = em vez disso ==, ele não será compilado, pois não é uma variável atribuível.
não sincronizado
47

Imagine que você tem esta matriz abaixo :

var arr = [1,2,3,4,5];

Javascript (navegadores novos e antigos):

function isArray(arr) {
  return arr.constructor.toString().indexOf("Array") > -1;
}

ou

function isArray(arr) {
  return arr instanceof Array;
}

ou

function isArray(arr) {
  return Object.prototype.toString.call(arr) === '[object Array]';
}

então chame assim:

isArray(arr);

Javascript (IE9 +, Ch5 +, FF4 +, Saf5 +, Opera10.5 +)

Array.isArray(arr);

jQuery:

$.isArray(arr);

Angular:

angular.isArray(arr);

Sublinhado e Lodash:

_.isArray(arr);
Alireza
fonte
34

Array.isArray funciona rápido, mas não é suportado por todas as versões dos navegadores. Então você pode fazer uma exceção para outras pessoas e usar o método universal:

    Utils = {};    
    Utils.isArray = ('isArray' in Array) ? 
        Array.isArray : 
        function (value) {
            return Object.prototype.toString.call(value) === '[object Array]';
        }
CruorVult
fonte
3
Você precisa obter o .toString()método Object.prototype. No momento, você está usando o window.toString(), que não é o mesmo.
o sistema
Você está certo. window.toStringfaça o mesmo que Object.prototype.toStringapenas no Chrome.
CruorVult
O isArray não é nada rápido. É o método mais lento.
jemiloii
Por que não adicioná-lo ao Array em vez de Utils? (Eu sei que você não quer propriedades extras sobre novos objetos de matriz, mas acho que só acontece se você adicionar IsArray para Array.prototype.)
David Winiecki
27

Função simples para verificar isso:

function isArray(object)
{
    return object.constructor === Array;
}
Meia-noiteTartaruga
fonte
16
Eu reduziria isso para uma linha return object.constructor === Array- mas você tem certeza de que isso só retornará verdadeiro para matrizes?
fácil
12
Pode fazer isso com todas as expressões booleanas. Me deixa louco quando vejo if(x) return true; else return false:-) Mesmo que seja ao contrário, você deve negar a expressão.
fácil
3
A razão pela qual isso não retorna true para getElementsByTagName, é porque o resultado dessa função é realmente um HTMLCollection e não uma matriz.
Yuval A.
6
Isso falhará se o objeto for indefinido ou nulo.
21715 John Henckel
1
@JohnHenckel Veja a minha resposta stackoverflow.com/a/29400289/34806 que leva em conta tanto a sua preocupação, bem como o primeiro comentário, tudo na mesma linha
Dexygen
17

Como a MDN diz aqui :

use Array.isArray ou Object.prototype.toString.call para diferenciar objetos regulares de matrizes

Como isso:

  • Object.prototype.toString.call(arr) === '[object Array]'ou

  • Array.isArray(arr)

ajax333221
fonte
17

Existe apenas uma solução de linha para esta pergunta

x instanceof Array

onde x é a variável, retornará true se x for uma matriz e false se não for.

Vikash Kumar
fonte
Muito mais limpo e seguro para o futuro! Isso ou uma typeofcomparação.
ChristoKiwi
Isso é algo que recebe uma boa quantidade de suporte ao navegador? Eu gosto disso.
Dan Zuzevich 20/08/19
1
Infelizmente, isso não é realmente útil sem uma tentativa / captura, porque se "x" é um objeto como {}uma matriz, você recebe um erro de sintaxe.
Abalter)
15

Você pode verificar o tipo de sua variável se é uma matriz com;

var myArray=[];

if(myArray instanceof Array)
{
....
}
Ahmet DAL
fonte
1
Algumas pessoas já mencionaram instanceof.. Acho que falha em alguns cenários estranhos.
MPEN
15

Eu faria uma função para testar o tipo de objeto com o qual você está lidando ...

function whatAmI(me){ return Object.prototype.toString.call(me).split(/\W/)[2]; }

// tests
console.log(
  whatAmI(["aiming","@"]),
  whatAmI({living:4,breathing:4}),
  whatAmI(function(ing){ return ing+" to the global window" }),
  whatAmI("going to do with you?")
);

// output: Array Object Function String

então você pode escrever uma declaração if simples ...

if(whatAmI(myVar) === "Array"){
    // do array stuff
} else { // could also check `if(whatAmI(myVar) === "String")` here to be sure
    // do string stuff
}
Billy Moon
fonte
12

Eu faço isso de uma maneira muito simples. Funciona para mim. Alguma desvantagem?

Array.prototype.isArray = true;

a=[]; b={};
a.isArray  // true
b.isArray  // (undefined -> false)
rsbkk
fonte
7
enganado por{isArray:true}
Bergi
JSON.parse(someDataFromElsewhere).items.isArraypode retornar true (dependendo dos dados) e quebrar seu código.
Roy Tinker
12

Esta é minha tentativa de melhorar esta resposta, levando em consideração os comentários:

var isArray = myArray && myArray.constructor === Array;

Ele se livra do if / else e explica a possibilidade de o array ser nulo ou indefinido

Dexygen
fonte
construtor não está disponível em ES5
TechTurtle
11

Atualizei o jsperf fiddle com dois métodos alternativos, bem como com a verificação de erros.

Acontece que o método que define um valor constante nos protótipos 'Objeto' e 'Matriz' é mais rápido que qualquer outro método. É um resultado um tanto surpreendente.

/* Initialisation */
Object.prototype.isArray = function() {
  return false;
};
Array.prototype.isArray = function() {
  return true;
};
Object.prototype._isArray = false;
Array.prototype._isArray = true;

var arr = ["1", "2"];
var noarr = "1";

/* Method 1 (function) */
if (arr.isArray()) document.write("arr is an array according to function<br/>");
if (!noarr.isArray()) document.write("noarr is not an array according to function<br/>");
/* Method 2 (value) - **** FASTEST ***** */
if (arr._isArray) document.write("arr is an array according to member value<br/>");
if (!noarr._isArray) document.write("noarr is not an array according to member value<br/>");

Esses dois métodos não funcionam se a variável receber o valor indefinido, mas funcionarão se você tiver certeza de que eles têm um valor. Com relação à verificação com o desempenho em mente se um valor é uma matriz ou um valor único, o segundo método se parece com um método rápido válido. É um pouco mais rápido que 'instanceof' no Chrome, duas vezes mais rápido que o segundo melhor método no Internet Explorer, Opera e Safari (na minha máquina).

le_top
fonte
9

Eu sei que as pessoas estão procurando algum tipo de abordagem javascript bruta. Mas se você quiser pensar menos, dê uma olhada aqui: http://underscorejs.org/#isArray

_.isArray(object) 

Retorna true se o objeto for uma matriz.

(function(){ return _.isArray(arguments); })();
=> false
_.isArray([1,2,3]);
=> true
Eugene
fonte
7
"A menos que outra tag para uma estrutura / biblioteca também esteja incluída, é esperada uma resposta JavaScript pura."
Michał Perłakowski
5

A melhor solução que eu vi é uma substituição entre navegadores do typeof. Confira a solução da Angus Croll aqui .

A versão TL; DR está abaixo, mas o artigo é uma ótima discussão sobre o problema; portanto, você deve lê-lo se tiver tempo.

Object.toType = function(obj) {
    return ({}).toString.call(obj).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
}
// ... and usage:
Object.toType([1,2,3]); //"array" (all browsers)

// or to test...
var shouldBeAnArray = [1,2,3];
if(Object.toType(shouldBeAnArray) === 'array'){/* do stuff */};
John Wundes
fonte
5

Aqui está minha abordagem preguiçosa:

if (Array.prototype.array_ === undefined) {
  Array.prototype.array_ = true;
}

// ...

var test = [],
    wat = {};

console.log(test.array_ === true); // true
console.log(wat.array_ === true);  // false

Sei que é um sacrilégio "mexer com" o protótipo, mas ele parece ter um desempenho significativamente melhor que o toStringmétodo recomendado .

Nota: Uma armadilha dessa abordagem é que ela não funcionará além dos iframelimites , mas para o meu caso de uso isso não é um problema.

namuol
fonte
não é mais melhor em termos de desempenho, pelo menos no FF30 no Ubuntu de 64 bits
test30
2
enganado por wat = {array_: true}objetos.
Bergi 7/08/2015
@ Bergi: Sim, isso deve ser óbvio. Se você estiver se preparando obj.array_ = true, estará apenas se enganando .
namuol
@namuol: Eu não estou necessariamente me enganando. Geralmente, objetos suficientes são usados ​​como dicionários. Pense em um cacheobjeto para memorizar os resultados da pesquisa que usa as cadeias de pesquisa como chaves de propriedade. E se um usuário pesquisar array_? Seu objeto se torna uma matriz por causa disso? É apenas um bug.
Bergi 08/08/2015
@namuol: Além disso, essa abordagem exigiria que todas as partes envolvidas (incluindo as bibliotecas usadas) possam concordar que .array_é usado para marcar matrizes. Isso realmente não é o caso aqui, .arraypode significar qualquer coisa. Você deve pelo menos usar uma sequência descritiva e sinalizar inadequação de uso arbitrário, por exemplo, com .__isArray = true.
Bergi 08/08/2015
5

Há um bom exemplo no livro JavaScript Patterns, de Stoyan Stefanov, que supõe lidar com todos os problemas possíveis, além de utilizar o método Array.isArray () do ECMAScript 5 .

Então aqui está:

if (typeof Array.isArray === "undefined") {
    Array.isArray = function (arg) {
        return Object.prototype.toString.call(arg) === "[object Array]";
    };
}

A propósito, se você estiver usando jQuery, poderá usar o método $ .isArray ()

Salvador Dalí
fonte
2
+1: por que não apenas um simples if(!Array.isArray) {...?
Marco Demaio
5

A maneira mais fácil e rápida de verificar se um Objeto é uma Matriz ou não.

 var arr = [];
  arr.constructor.name ==='Array'  //return true;

ou

arr.constructor ===Array //return true;

ou você pode fazer uma função de utilitário:

function isArray(obj){ return obj && obj.constructor ===Array}

uso:

isArray(arr); //return true
sheelpriy
fonte
5

O seguinte pode ser usado se você souber que seu objeto não possui um método concat.

var arr = [];
if (typeof arr.concat === 'function') {
    console.log("It's an array");
}

yesil
fonte
Este é um truque bom, mas poderia ser substituído ... mas na maioria das vezes deve obter o resultado
Alireza
5

Você poderia é o método isArray, mas eu preferiria verificar com

Object.getPrototypeOf(yourvariable) === Array.prototype

AÇO
fonte
Por que você prefere isso?
MPEN
@mpen Object.getPrototypeOf(yourvariable)retorna o protótipo de um objeto Array. E o código é mais rápido e seguro.
STEEL
1
É fácil frustrar: pastebin.com/MP8d5bCE Além disso, você tem testes de desempenho para fazer backup de sua reivindicação "mais rápida"?
MPEN
4

Se os únicos dois tipos de valores que podem ser passados ​​para essa função são uma sequência ou uma matriz de sequências, mantenha-a simples e use uma typeofverificação da possibilidade de sequência:

function someFunc(arg) {
    var arr = (typeof arg == "string") ? [arg] : arg;
}
Tim Down
fonte
Sim ... isso funcionaria para esse cenário, mas não em geral. Acabou usando varargs de qualquer maneira. :)
MPEN
4
A = [1,2,3]
console.log(A.map==[].map)

Em busca da versão mais curta, aqui está o que eu consegui até agora.

Observe que não existe uma função perfeita que sempre detecte todas as combinações possíveis. É melhor conhecer todas as habilidades e limitações de suas ferramentas do que esperar uma ferramenta mágica.

exebook
fonte
1
ligeira derivação da mina A.map !== undefined, mas sim, que poderia ser estrada escorregadio no mundo do macaco patchers;)
dmi3y
FYI: Isso não funciona entre os iFrames ( stackoverflow.com/questions/460256/… ) #
WiredPrairie
4
function isArray(value) {
    if (value) {
        if (typeof value === 'object') {
            return (Object.prototype.toString.call(value) == '[object Array]')
        }
    }
    return false;
}

var ar = ["ff","tt"]
alert(isArray(ar))
RoboTamer
fonte
4

Uma função simples para testar se um valor de entrada é uma matriz é o seguinte:

function isArray(value)
{
  return Object.prototype.toString.call(value) === '[object Array]';
}

Isso funciona em vários navegadores e com navegadores mais antigos. Isso é retirado da postagem do blog de TJ Crowders

Brad Parks
fonte
4

Você pode tentar isso:

var arr = []; (or) arr = new Array();
var obj = {}; (or) arr = new Object();

arr.constructor.prototype.hasOwnProperty('push') //true

obj.constructor.prototype.hasOwnProperty('push') // false
VIJAY P
fonte
4

Esta função transformará quase tudo em uma matriz:

function arr(x) {
    if(x === null || x === undefined) {
        return [];
    }
    if(Array.isArray(x)) {
        return x;
    }
    if(isString(x) || isNumber(x)) {
        return [x];
    }
    if(x[Symbol.iterator] !== undefined || x.length !== undefined) {
        return Array.from(x);
    }
    return [x];
}

function isString(x) {
    return Object.prototype.toString.call(x) === "[object String]"
}

function isNumber(x) {
    return Object.prototype.toString.call(x) === "[object Number]"
}

Ele usa alguns recursos mais recentes do navegador, portanto, você pode preencher isso para obter o máximo suporte.

Exemplos:

> arr(null);
[]
> arr(undefined)
[]
> arr(3.14)
[ 3.14 ]
> arr(1/0)
[ Infinity ]
> gen = function*() { yield 1; yield 2; yield 3; }
[Function: gen]
> arr(gen())
[ 1, 2, 3 ]
> arr([4,5,6])
[ 4, 5, 6 ]
> arr("foo")
[ 'foo' ]

NB seqüências serão convertidas em uma matriz com um único elemento em vez de uma matriz de caracteres. Exclua a isStringverificação se você preferir o contrário.

Eu usei Array.isArrayaqui porque é o mais robusto e também o mais simples.

mpen
fonte
4

No seu caso, você pode usar o concatmétodo Array, que pode aceitar objetos únicos e também array (e até combinado):

function myFunc(stringOrArray)
{
  var arr = [].concat(stringOrArray);

  console.log(arr);

  arr.forEach(function(item, i)
  {
    console.log(i, "=", item);
  })
}

myFunc("one string");

myFunc(["one string", "second", "third"]);

concat parece ser um dos métodos mais antigos do Array (até o IE 5.5 o conhece bem).

kolyaseg
fonte