Crie uma matriz com o mesmo elemento repetido várias vezes

323

No Python, onde [2]está uma lista, o código a seguir fornece essa saída:

[2] * 5 # Outputs: [2,2,2,2,2]

Existe uma maneira fácil de fazer isso com uma matriz em JavaScript?

Eu escrevi a seguinte função para fazê-lo, mas há algo mais curto ou melhor?

var repeatelem = function(elem, n){
    // returns an array with element elem repeated n times.
    var arr = [];

    for (var i = 0; i <= n; i++) {
        arr = arr.concat(elem);
    };

    return arr;
};
Curious2learn
fonte
4
Possível duplicata: stackoverflow.com/questions/1295584/…
Larry Battle

Respostas:

52

Você pode fazer assim:

function fillArray(value, len) {
  if (len == 0) return [];
  var a = [value];
  while (a.length * 2 <= len) a = a.concat(a);
  if (a.length < len) a = a.concat(a.slice(0, len - a.length));
  return a;
}

Dobra a matriz em cada iteração, para criar uma matriz muito grande com poucas iterações.


Nota: Você também pode melhorar bastante sua função usando em pushvez de concat, pois concatcriará uma nova matriz a cada iteração. Assim (mostrado apenas como um exemplo de como você pode trabalhar com matrizes):

function fillArray(value, len) {
  var arr = [];
  for (var i = 0; i < len; i++) {
    arr.push(value);
  }
  return arr;
}
Guffa
fonte
11
É muito mais eficiente alocar todas as entradas antecipadamente, arr = new Array(len);atribuir a elas pelo índice no loop, ou seja arr[i] = value;. Dessa forma, você paga apenas O (n) em vez de precisar realocar a matriz à medida que ela cresce. Em geral, é sempre melhor evitar o aumento de matrizes anexando quando possível. Se você souber o tamanho final, use-o.
Tom Karzes
26
Array.from({length:5}).map(x => 2)
noobninja
1
@noobninja: ótima solução; você deve digitá-lo novamente como resposta para que possa ser votado.
jsalvata
693

Em ES6 usando matriz de enchimento () Método

Array(5).fill(2)
//=> [2, 2, 2, 2, 2]
Niko Ruotsalainen
fonte
3
O Internet Explorer e o Opera ainda não são compatíveis.
DenisKolodin
4
O Node.js <= 3 não suporta isso.
Li
1
@DenisKolodin O Opera atual faz. E Edge também. Veja compat-table
Janus Troelsen
4
@ Michael Você pode fazer isso com Array(5).fill([1,2,3]).flat(). Observe que flat () ainda é muito experimental e o suporte ao navegador é ruim.
Niko Ruotsalainen
3
Observe que o argumento to fillé avaliado uma vez; portanto, Array(9).fill(someMutable)cria nove referências someMutableno array (a mutação de uma mutará 'todas elas').
Carl Smith
146
>>> Array.apply(null, Array(10)).map(function(){return 5})
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
>>> //Or in ES6
>>> [...Array(10)].map((_, i) => 5)
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
Janus Troelsen
fonte
6
Pergunta boba, mas por que o novo Array (10) .map não funciona?
#
1
blazkovicz, veja o comentário de zertosh nesta resposta pelo motivo.
21815 Ross Rogers
5
Com o ES6, isso pode ser "atualizado" para Array(...Array(10)).map(() => 5)o operador de spread ou também paraArray.from(Array(10)).map(() => 5)
Christophe Vidal
4
De acordo com a MDN , você pode fazer o seguinte:Array.from({length: 10}, () => 5);
Plusb Preco
2
@blazkovicz A matriz (10) cria uma matriz lenght = 10sem propriedades enumeráveis. .mapfunciona apenas em propriedades enumeráveis. O .applymétodo pega essa matriz e mapeia-a em um arguments array(um objeto semelhante a uma matriz) a ser passado para a Arrayfunção construtora. A matriz de argumentos não pode ser esparsa, portanto, as propriedades ausentes são definidas undefinede se tornam enumeráveis. Agora podemos usar .mapcomo pretendido
Cerved
91

podes tentar:

Array(6).join('a').split(''); // returns ['a','a','a','a','a'] (5 times)

Atualização (01/06/2018) :

Agora você pode repetir um conjunto de caracteres.

new Array(5).fill('a'); // give the same result as above;
// or
Array.from({ length: 5 }).fill('a')

Nota: Verifique mais sobre preenchimento (...) e de (...) para compatibilidade e suporte ao navegador.

Atualização (11/05/2019) :

Outra maneira, sem usar fillou from, que funciona para cadeias de caracteres de qualquer comprimento:

Array.apply(null, Array(3)).map(_ => 'abc') // ['abc', 'abc', 'abc']

O mesmo que a resposta acima . Adicionando por uma questão de integridade.

Vivek
fonte
1
Agradável. Mais curto e mais simples.
Johann Echavarria 27/10
2
+1 para manipulação simples. Uma pena que você não possa criar uma matriz de cadeias vazias com isso. Além de muito esperto ... #
28514 Quicker
1
A matriz (6) .join ('a'). Split ('a') fornece uma matriz de string vazia.
Vivek
17
Isso funciona apenas para 1 string de caracteres, portanto, não responde completamente à pergunta.
az_
3
@AlfonsoVergara: Em Javascript, String é um tipo primitivo (semântico de valor); não há como obter duas strings para referenciar os mesmos dados (porque strings não são referências em Javascript; são valores). Você precisa observar a semântica de referência com objetos e listas, mas não com seqüências de caracteres.
Quuxplusone
36

Array.from({length:5}, i => 1) // [1, 1, 1, 1, 1]

ou criar matriz com valor crescente

Array.from({length:5}, (e, i)=>i) // [0, 1, 2, 3, 4]

Thomson
fonte
Bom, 👍 essa é a melhor abordagem se você precisar que o elemento seja dinâmico
#
Array.from({length:5}, i => 1)=> ié undefinedporque representa valor vazio Array.from({length:5}, (e, i)=>i)=> eé undefinedporque representa valor vazio. Deve ser Array.from({length:5}, v => 1)eArray.from({length:5}, (v, i)=>i)
Rafal Enden
33

No lodash não é tão ruim:

_.flatten(_.times(5, function () { return [2]; }));
// [2, 2, 2, 2, 2]

EDIT : Ainda melhor:

_.times(5, _.constant(2));
// [2, 2, 2, 2, 2]

EDIT : Ainda melhor:

_.fill(Array(5), 2);
Droogans
fonte
3
_.times (length, _.constant (value))
user1533401
1
dos documentos: _.fill (Matriz (3), 2); Que não está longe de ES6
kert
15

[c] * n pode ser escrito como:

Array(n+1).join(1).split('').map(function(){return c;})

então para [2] * 5

Array(6).join(1).split('').map(function(){return 2;})
brook hong
fonte
Na verdade, a matriz (6) .join (2) .split ('') seria mais fácil? Por que você precisaria do mapa?
Angela
4
que retorna ["2", "2", "2", "2", "2"], em vez de [2, 2, 2, 2, 2].
brook hong
10

Você também pode estender a funcionalidade do Array da seguinte maneira:

Array.prototype.fill = function(val){
    for (var i = 0; i < this.length; i++){
        this[i] = val;
    }
    return this;
};
// used like:
var arry = new Array(5)​.fill(2);
// or
var arry = new Array(5);
arry.fill(2);


console.log(arry);​ //[2, 2, 2, 2, 2] 

Devo observar que estender a funcionalidade de objetos internos pode causar problemas se você estiver trabalhando com bibliotecas de terceiros. Sempre avalie isso em suas decisões.

Shmiddty
fonte
Observe que, se você passar um objeto para fillcada índice da matriz, se referirá ao mesmo objeto, portanto, alterar um mudará todos eles. Isso não é muito difícil de resolver se se tornar um problema.
Shmiddty 19/09/12
Não entendo por que muitos usuários de javascript desconhecem o requisito de não protótipo de funções nativas.
brooNo
2
Um método de preenchimento foi adicionado no ES6. Portanto, eu não recomendaria adicionar suas próprias coisas ao protótipo, você substituirá a versão mais rápida e capaz.
Janus Troelsen
1
@JanusTroelsen Para evitar a substituição de bibliotecas javascript ou de terceiros, você pode verificar se existe antes de declará-lo. if (!Array.prototype.fill) { }
Oliver
1
@JanusTroelsen O que você quer dizer com "real polyfill" ?. Eu usei um polifyll. Quero dizer: verifique se há detecção e implementação de recursos, caso contrário.
Oliver
5

Caso você precise repetir uma matriz várias vezes:

var arrayA = ['a','b','c'];
var repeats = 3;
var arrayB = Array.apply(null, {length: repeats * arrayA.length})
        .map(function(e,i){return arrayA[i % arrayA.length]});
// result: arrayB = ['a','b','c','a','b','c','a','b','c']

inspirado por esta resposta

Henrik Christensen
fonte
em ES6, você pode fazer "abc" .repeat (3)
Janus Troelsen
4

Não há maneira mais fácil. Você precisa fazer um loop e inserir elementos na matriz.

Diodeus - James MacFarlane
fonte
Obrigado! Antes de aceitar, é pushmelhor ou concatmelhor, em termos de eficiência?
Curious2learn
O CONCAT precisa inspecionar duas matrizes, PUSH apenas adiciona outro elemento, então eu esperaria que PUSH fosse mais eficiente em geral, mas para os DADOS IDENTICOS, acho que a resposta de Guffa é a melhor.
Diodeus - James MacFarlane 19/09/12
Não há necessidade de loops, veja minha resposta.
Janus Troelsen
@JanusTroelsen, não é necessário fazer um loop, a menos que você queira uma maneira eficiente de executar esta ação. A sua é uma das mais lentas que você pode encontrar. push () para [] é o mais rápido.
chrilith
4

Use esta função:

function repeatElement(element, count) {
    return Array(count).fill(element)
}
>>> repeatElement('#', 5).join('')
"#####"

Ou para uma versão mais compacta:

const repeatElement = (element, count) =>
    Array(count).fill(element)
>>> repeatElement('#', 5).join('')
"#####"

Ou para uma versão com capacidade para curry:

const repeatElement = element => count =>
    Array(count).fill(element)
>>> repeatElement('#')(5).join('')
"#####"

Você pode usar esta função com uma lista:

const repeatElement = (element, count) =>
    Array(count).fill(element)

>>> ['a', 'b', ...repeatElement('c', 5)]
['a', 'b', 'c', 'c', 'c', 'c', 'c']
Ken Mueller
fonte
4

Se você precisar repetir uma matriz, use o seguinte.

Array.from({ length: 3 }).fill(['a','b','c']).flat()

retornará

Array(9) [ "a", "b", "c", "a", "b", "c", "a", "b", "c" ]
etoxina
fonte
ótimo oneliner usando uma matriz existente, o que eu precisava.
gneric
2

Outra linha:

Array.prototype.map.call([]+Array(5+1),function(){ return '2'; })
Filip Hermans
fonte
1

Essa função cria uma matriz de elementos (comprimento) em que cada elemento é igual (valor) desde que (valor) seja um número inteiro ou sequência de caracteres de um número inteiro. Quaisquer números decimais serão truncados. Se você deseja números decimais, substitua "parseInt ( " por " parseFloat ( "

function fillArray(length, intValue) {
     var vals = (new Array(length + 1)).join(intValue + '|').split('|').slice(0,length);
     for(var i = 0; i < length; i += 1) {
         vals[i] = parseInt(vals[i]);
     }
     return vals;
}

Exemplos:

fillArray(5, 7) // returns [7,7,7,7,7]
fillArray(5, 7.5) // returns [7,7,7,7,7]
fillArray(5, 200) // returns [200,200,200,200,200]
TxRegex
fonte
1

Eu tive problemas com os métodos mencionados quando uso uma matriz como

var array = ['foo', 'bar', 'foobar'];
var filled = array.fill(7);

//filled should be ['foo', 'bar', 'foobar', 'foo', 'bar', 'foobar', 'foo']

Para conseguir isso, estou usando:

Array.prototype.fill = function(val){
    var l = this.length;
    if(l < val){
        for(var i = val-1-l; i >= 0; i--){
            this[i+l] = this[i % l];
        }
    }
    return this;
};
Xaver
fonte
Isso é legal, mas provavelmente deve ser nomeado cycle.
Drenmi
1

Eu descobri isso hoje enquanto tentava fazer uma matriz 2D sem usar loops. Em retrospecto, juntar uma nova matriz é legal; Tentei mapear uma nova matriz, que não funciona, pois o mapa ignora os slots vazios.

"#".repeat(5).split('').map(x => 0)

O caracter "#" pode ser qualquer caractere único válido. O 5 seria uma variável para o número de elementos que você deseja. O 7 seria o valor com o qual você deseja preencher sua matriz.

O novo método de preenchimento é melhor e, quando codifiquei isso, não sabia que ele existia, nem sabia que repetir é es6; Vou escrever um post sobre como usar esse truque em conjunto com reduzir para fazer coisas legais.

http://jburger.us.to/2016/07/14/functionally-create-a-2d-array/

Magical Gordon
fonte
1

Tente isto:

"avinash ".repeat(5).trim().split(" ");
Avinash
fonte
1

var finalAry = [..."2".repeat(5).split("")].map(Number);
console.log(finalAry);

Amaldev ps
fonte
0

Se você estiver usando um cinto de utilidade como lodash / underscore, poderá fazê-lo assim :)

let result = _.map(_.times(foo), function() {return bar})
Shane Rogers
fonte
0

Também pode ser usado como uma linha:

function repeat(arr, len) {
    while (arr.length < len) arr = arr.concat(arr.slice(0, len-arr.length));
    return arr;
}
Salsaparrilha
fonte
0

Melhorando a resposta de Vivek , isso funciona para cadeias de qualquer comprimento, para preencher uma matriz de comprimento n:Array(n+1).join('[string to be repeated][separator]').split('[separator]').slice(0, n)

Tigregalis
fonte
-1

Eu precisava de uma maneira de repetir / repetir uma matriz (com n itens) m vezes.

Por exemplo, distribuindo uma lista (de pessoas) para uma semana / mês. Digamos que eu tenho 3 nomes e quero que eles repitam em uma semana:

fillArray(["Adam", "Blair", "Curtis"], 7); // returns ["Adam", "Blair", "Curtis", "Adam", "Blair", "Curtis", "Adam"]

function fillArray(pattern, count) {
    let result = [];
    if (["number", "string"].includes(typeof pattern)) {
        result = new Array(5);
        result.fill(pattern);
    }
    else if (pattern instanceof Array) {
        for (let i = 0; i < count; i++) {
            result = result.concat(pattern);
        }
        result = result.slice(0, count);
    }
    return result;
}

fillArray("a", 5);        // ["a", "a", "a", "a", "a"]
fillArray(1, 5);          // [1, 1, 1, 1, 1]
fillArray(["a", "b"], 5); // ["a", "b", "a", "b", "a"]
akinuri
fonte