Repetir Cadeia – Javascript

Qual é o método melhor ou mais conciso para retornar uma string repetida uma quantidade arbitrária de vezes?

O seguinte é o meu melhor tiro até agora:

function repeat(s, n){ var a = []; while(a.length < n){ a.push(s); } return a.join(''); } 

Nota para novos leitores: Esta resposta é antiga e não é muito prática – é apenas “inteligente” porque usa as coisas do Array para fazer as coisas do String. Quando eu escrevi “menos processo” eu definitivamente quis dizer “menos código” porque, como outros notaram nas respostas subsequentes, ele funciona como um porco. Então não use se a velocidade é importante para você.

Eu coloquei essa function no object String diretamente. Em vez de criar uma matriz, preenchê-la e uni-la a um caractere vazio, basta criar uma matriz com o comprimento adequado e associá-la à sua string desejada. Mesmo resultado, menos processo!

 String.prototype.repeat = function( num ) { return new Array( num + 1 ).join( this ); } alert( "string to repeat\n".repeat( 4 ) ); 

Eu testei o desempenho de todas as abordagens propostas.

Aqui está a variante mais rápida que eu tenho.

 String.prototype.repeat = function(count) { if (count < 1) return ''; var result = '', pattern = this.valueOf(); while (count > 1) { if (count & 1) result += pattern; count >>= 1, pattern += pattern; } return result + pattern; }; 

Ou como function autônoma :

 function repeat(pattern, count) { if (count < 1) return ''; var result = ''; while (count > 1) { if (count & 1) result += pattern; count >>= 1, pattern += pattern; } return result + pattern; } 

É baseado no algoritmo artistoex . É muito rápido. E quanto maior a count , mais rápido ele é comparado com a new Array(count + 1).join(string) abordagem tradicional de new Array(count + 1).join(string) .

Eu só mudei 2 coisas:

  1. pattern = this substituído pattern = this com pattern = this.valueOf() (limpa uma conversão de tipo óbvia);
  2. adicionado if (count < 1) verifica de prototypejs para o topo da function para excluir ações desnecessárias nesse caso.
  3. otimização aplicada da resposta de Dennis (velocidade de 5 a 7%)

UPD

Criou um pequeno parque infantil de teste de desempenho para os interessados.

count variável ~ 0 .. 100:
Testando o desempenho de diferentes variantes de String.repeat () http://tinyurl.com/kb3raxr

count constante = 1024:
Teste de desempenho de diferentes variantes de String.repeat () http://tinyurl.com/k527auo

Use-o e torne-o ainda mais rápido se puder 🙂

Esse problema é um problema de otimização conhecido / “clássico” para JavaScript, causado pelo fato de que as cadeias JavaScript são “imutáveis” e a adição por concatenação de até mesmo um único caractere a uma cadeia requer a criação de, incluindo alocação de memory e cópia para , uma nova string inteira.

Infelizmente, a resposta aceita nesta página é errada, onde “errado” significa um fator de desempenho de 3x para strings simples de um caractere e 8x-97x para strings curtas repetidas mais vezes, para 300x para sentenças repetidas e infinitamente errado quando tomando o limite das razões de complexidade dos algoritmos como n vai para o infinito. Além disso, há outra resposta nesta página que está quase certa (baseada em uma das muitas gerações e variações da solução correta que circula pela Internet nos últimos 13 anos). No entanto, esta solução “quase certa” perde um ponto-chave do algoritmo correto, causando 50% de degradação no desempenho.

JS Performance Results para a resposta aceita, a outra resposta com melhor desempenho (com base em uma versão degradada do algoritmo original nesta resposta) e essa resposta usando meu algoritmo criado há 13 anos

~ Outubro de 2000 Eu publiquei um algoritmo para esse problema exato que foi amplamente adaptado, modificado, e eventualmente mal compreendido e esquecido. Para remediar este problema, em agosto de 2008 eu publiquei um artigo http://www.webreference.com/programming/javascript/jkm3/3.html explicando o algoritmo e usando-o como um exemplo de simples otimizações de JavaScript de propósito geral. Até agora, a Web Reference removeu minhas informações de contato e até mesmo meu nome deste artigo. E mais uma vez, o algoritmo foi amplamente adaptado, modificado, mal compreendido e amplamente esquecido.

Algoritmo JavaScript de repetição / multiplicação de string original por Joseph Myers, cerca de Y2K como uma function de multiplicação de texto dentro de Text.js; publicado em agosto de 2008 nesta forma pela Web Reference: http://www.webreference.com/programming/javascript/jkm3/3.html (O artigo usou a function como um exemplo de otimizações de JavaScript, que é o único para o estranho nome “stringFill3”.)

 /* * Usage: stringFill3("abc", 2) == "abcabc" */ function stringFill3(x, n) { var s = ''; for (;;) { if (n & 1) s += x; n >>= 1; if (n) x += x; else break; } return s; } 

Dentro de dois meses após a publicação desse artigo, essa mesma pergunta foi postada no Stack Overflow e voou sob meu radar até agora, quando aparentemente o algoritmo original para esse problema foi novamente esquecido. A melhor solução disponível nesta página Stack Overflow é uma versão modificada da minha solução, possivelmente separada por várias gerações. Infelizmente, as modificações arruinaram a otimização da solução. Na verdade, alterando a estrutura do loop do meu original, a solução modificada executa uma etapa extra desnecessariamente desnecessária de duplicação exponencial (unindo, assim, a maior string usada na resposta apropriada a si mesma por um tempo extra e descartando-a).

Abaixo segue uma discussão de algumas otimizações de JavaScript relacionadas a todas as respostas para este problema e para o benefício de todos.

Técnica: Evite referências a objects ou propriedades de objects

Para ilustrar como essa técnica funciona, usamos uma function JavaScript real que cria strings de qualquer tamanho necessário. E, como veremos, mais otimizações podem ser adicionadas!

Uma function como a usada aqui é criar preenchimento para alinhar colunas de texto, para formatar dinheiro ou para preencher dados de bloco até o limite. Uma function de geração de texto também permite input de comprimento variável para testar qualquer outra function que opera no texto. Essa function é um dos componentes importantes do módulo de processamento de texto JavaScript.

À medida que avançamos, estaremos cobrindo mais duas das mais importantes técnicas de otimização enquanto desenvolvemos o código original em um algoritmo otimizado para criar strings. O resultado final é uma function de alto desempenho e força industrial que usei em todos os lugares – alinhando preços e totais de itens em formulários de pedidos JavaScript, formatação de dados e formatação de mensagens de texto / e-mail e muitos outros usos.

Código original para criar strings stringFill1()

 function stringFill1(x, n) { var s = ''; while (s.length < n) s += x; return s; } /* Example of output: stringFill1('x', 3) == 'xxx' */ 

A syntax está aqui é clara. Como você pode ver, já usamos variables ​​de function locais antes de continuar com mais otimizações.

Esteja ciente de que há uma referência inocente a uma propriedade de object s.length no código que prejudica seu desempenho. Pior ainda, o uso dessa propriedade de object reduz a simplicidade do programa, supondo que o leitor saiba sobre as propriedades dos objects de cadeia de caracteres JavaScript.

O uso dessa propriedade de object destrói a generalidade do programa de computador. O programa assume que x deve ser uma cadeia de comprimento um. Isso limita a aplicação da function stringFill1() a qualquer coisa, exceto a repetição de caracteres únicos. Mesmo caracteres únicos não podem ser usados ​​se contiverem vários bytes, como a entidade HTML   .

O pior problema causado por este uso desnecessário de uma propriedade de object é que a function cria um loop infinito se testado em uma cadeia de input vazia x . Para verificar a generalidade, aplique um programa à menor quantidade possível de input. Um programa que falha quando solicitado a exceder a quantidade de memory disponível tem uma desculpa. Um programa como este que falha quando solicitado a produzir nada é inaceitável. Às vezes, um código bonito é um código venenoso.

A simplicidade pode ser um objective ambíguo da programação de computadores, mas geralmente não é. Quando um programa não tem um nível razoável de generalidade, não é válido dizer: "O programa é bom o suficiente até onde vai." Como você pode ver, o uso da propriedade string.length impede que esse programa funcione em uma configuração geral e, de fato, o programa incorreto está pronto para causar um travamento do navegador ou do sistema.

Existe uma maneira de melhorar o desempenho deste JavaScript, bem como cuidar desses dois problemas sérios?

Claro. Apenas use números inteiros.

Código otimizado para criar strings stringFill2()

 function stringFill2(x, n) { var s = ''; while (n-- > 0) s += x; return s; } 

Código de tempo para comparar stringFill1() e stringFill2()

 function testFill(functionToBeTested, outputSize) { var i = 0, t0 = new Date(); do { functionToBeTested('x', outputSize); t = new Date() - t0; i++; } while (t < 2000); return t/i/1000; } seconds1 = testFill(stringFill1, 100); seconds2 = testFill(stringFill2, 100); 

O sucesso até agora de stringFill2()

stringFill1() leva 47,297 microssegundos (milionésimos de segundo) para preencher uma string de 100 bytes, e stringFill2() leva 27,68 microssegundos para fazer a mesma coisa. Isso é quase o dobro do desempenho, evitando uma referência a uma propriedade de object.

Técnica: evite adicionar strings curtas a strings longas

Nosso resultado anterior parecia bom - muito bom, na verdade. A function aprimorada stringFill2() é muito mais rápida devido ao uso de nossas duas primeiras otimizações. Você acreditaria se eu lhe dissesse que pode ser melhorado para ser muitas vezes mais rápido do que é agora?

Sim, podemos alcançar esse objective. No momento, precisamos explicar como evitamos append strings curtas a strings longas.

O comportamento a curto prazo parece ser muito bom, em comparação com a nossa function original. Cientistas da computação gostam de analisar o "comportamento assintótico" de uma function ou algoritmo de programa de computador, o que significa estudar seu comportamento de longo prazo testando-o com insumos maiores. Às vezes, sem fazer mais testes, nunca se percebe maneiras de melhorar um programa de computador. Para ver o que vai acontecer, vamos criar uma string de 200 bytes.

O problema que aparece com stringFill2()

Usando nossa function de temporização, descobrimos que o tempo aumenta para 62,54 microssegundos para uma string de 200 bytes, em comparação com 27,68 para uma string de 100 bytes. Parece que o tempo deveria ser dobrado por fazer o dobro de trabalho, mas triplicado ou quadruplicado. Da experiência de programação, este resultado parece estranho, porque se alguma coisa, a function deve ser ligeiramente mais rápida desde que o trabalho está sendo feito mais eficientemente (200 bytes por chamada de function em vez de 100 bytes por chamada de function). Esse problema tem a ver com uma propriedade insidiosa de strings JavaScript: strings JavaScript são "imutáveis".

Imutável significa que você não pode alterar uma string depois de criada. Ao adicionar um byte de cada vez, não estamos usando mais um byte de esforço. Na verdade, estamos recriando a string inteira e mais um byte.

Na verdade, para adicionar mais um byte a uma cadeia de 100 bytes, são necessários 101 bytes de trabalho. Vamos analisar brevemente o custo computacional para criar uma string de N bytes. O custo de adicionar o primeiro byte é uma unidade de esforço computacional. O custo de adicionar o segundo byte não é uma unidade, mas sim duas unidades (copiar o primeiro byte para um novo object string e adicionar o segundo byte). O terceiro byte requer um custo de 3 unidades, etc.

C(N) = 1 + 2 + 3 + ... + N = N(N+1)/2 = O(N^2) . O símbolo O(N^2) é pronunciado Big O de N ao quadrado e significa que o custo computacional a longo prazo é proporcional ao quadrado do comprimento da cadeia. Para criar 100 caracteres, são necessárias 10.000 unidades de trabalho e, para criar 200 caracteres, são necessárias 40.000 unidades de trabalho.

É por isso que demorou mais do que o dobro para criar 200 caracteres do que 100 caracteres. De fato, deveria ter levado quatro vezes mais tempo. Nossa experiência de programação estava correta, pois o trabalho está sendo feito com um pouco mais de eficiência para seqüências mais longas e, portanto, levava apenas três vezes mais tempo. Uma vez que a sobrecarga da chamada de function se torna desprezível quanto ao tempo de uma string que estamos criando, ela realmente levará quatro vezes mais tempo para criar uma string duas vezes mais longa.

(Nota histórica: Esta análise não se aplica necessariamente a strings no código-fonte, como html = 'abcd\n' + 'efgh\n' + ... + 'xyz.\n' , já que o compilador de código fonte do JavaScript pode unir as strings antes de transformá-las em um object de string JavaScript.Apenas alguns anos atrás, a implementação de JavaScript do KJS congelaria ou travaria ao carregar longas cadeias de código-fonte unidas por sinais de adição, já que o tempo computacional era O(N^2) não era difícil criar páginas da Web que sobrecarregassem o navegador da Web Konqueror ou o Safari, que usava o núcleo do mecanismo KJS JavaScript. Eu me deparei com essa questão quando desenvolvia uma linguagem de marcação e um analisador de linguagem JavaScript markup. descobri o que estava causando o problema quando escrevi meu script para JavaScript Includes.)

Claramente, essa rápida degradação do desempenho é um problema enorme. Como podemos lidar com isso, uma vez que não podemos alterar a maneira como o JavaScript manipula strings como objects imutáveis? A solução é usar um algoritmo que recria a string o menor número de vezes possível.

Para esclarecer, nosso objective é evitar adicionar strings curtas a strings longas, já que para adicionar a string curta, toda a string longa também deve ser duplicada.

Como o algoritmo funciona para evitar adicionar strings curtas a strings longas

Aqui está uma boa maneira de reduzir o número de vezes que novos objects de string são criados. Concatene comprimentos mais longos de string juntos para que mais de um byte de cada vez seja adicionado à saída.

Por exemplo, para fazer uma string de comprimento N = 9 :

 x = 'x'; s = ''; s += x; /* Now s = 'x' */ x += x; /* Now x = 'xx' */ x += x; /* Now x = 'xxxx' */ x += x; /* Now x = 'xxxxxxxx' */ s += x; /* Now s = 'xxxxxxxxx' as desired */ 

Isso exigia a criação de uma cadeia de comprimento 1, a criação de uma cadeia de comprimento 2, a criação de uma cadeia de comprimento 4, a criação de uma cadeia de comprimento 8 e a criação de uma cadeia de comprimento 9. Quanto poupamos?

Custo antigo C(9) = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 9 = 45 .

Novo custo C(9) = 1 + 2 + 4 + 8 + 9 = 24 .

Note que tivemos que adicionar uma string de comprimento 1 a uma string de comprimento 0, depois uma string de comprimento 1 para uma string de comprimento 1, depois uma string de comprimento 2 para uma string de comprimento 2, depois uma string de comprimento 4 para uma cadeia de comprimento 4, depois uma cadeia de comprimento 8 para uma cadeia de comprimento 1, para obter uma cadeia de comprimento 9. O que estamos a fazer pode ser resumido como evitar adicionar cadeias curtas a cadeias longas ou noutras palavras, tentando concatenar cadeias de caracteres que são de comprimento igual ou quase igual.

Para o antigo custo computacional encontramos uma fórmula N(N+1)/2 . Existe uma fórmula para o novo custo? Sim, mas é complicado. O importante é que é O(N) , e assim dobrar o comprimento da corda irá aproximadamente dobrar a quantidade de trabalho ao invés de quadruplicá-lo.

O código que implementa essa nova ideia é quase tão complicado quanto a fórmula para o custo computacional. Ao lê-lo, lembre-se de que >>= 1 significa mudar para a direita em 1 byte. Então, se n = 10011 é um número binário, então n >>= 1 resulta no valor n = 1001 .

A outra parte do código que você talvez não reconheça é o bit a bit eo operador, escrito & . A expressão n & 1 avalia true se o último dígito binário de n for 1 e falso se o último dígito binário de n for 0.

Nova function stringFill3() altamente eficiente

 function stringFill3(x, n) { var s = ''; for (;;) { if (n & 1) s += x; n >>= 1; if (n) x += x; else break; } return s; } 

Parece feio para o olho destreinado, mas seu desempenho é nada menos que adorável.

Vamos ver o quão bem esta function funciona. Depois de ver os resultados, é provável que você nunca esqueça a diferença entre um algoritmo O(N^2) e um algoritmo O(N) .

stringFill1() leva 88,7 microssegundos (milionésimos de segundo) para criar uma string de 200 bytes, stringFill2() leva 62,54 e stringFill3() leva apenas 4,608. O que tornou esse algoritmo muito melhor? Todas as funções aproveitaram o uso de variables ​​de function locais, mas o aproveitamento da segunda e da terceira técnicas de otimização adicionou uma melhoria de vinte vezes ao desempenho de stringFill3() .

Análise mais profunda

O que faz com que essa function específica exploda a competição fora da água?

Como eu mencionei, o motivo pelo qual essas duas funções, stringFill1() e stringFill2() , são executadas tão lentamente é que as strings JavaScript são imutáveis. A memory não pode ser realocada para permitir que mais um byte de cada vez seja anexado aos dados de string armazenados pelo JavaScript. Toda vez que mais um byte é adicionado ao final da string, toda a string é regenerada do começo ao fim.

Assim, para melhorar o desempenho do script, é necessário pré-compilar strings de comprimento mais longo, concatenando duas strings juntas antes do tempo e, em seguida, criando recursivamente o comprimento de string desejado.

Por exemplo, para criar uma string de 16 letras, primeiro uma string de dois bytes seria pré-computada. Em seguida, a cadeia de dois bytes seria reutilizada para pré-compilar uma cadeia de quatro bytes. Em seguida, a cadeia de quatro bytes seria reutilizada para pré-calcular uma cadeia de oito bytes. Finalmente, duas seqüências de oito bytes seriam reutilizadas para criar a nova seqüência desejada de 16 bytes. Ao todo quatro novas cadeias tiveram que ser criadas, uma de comprimento 2, uma de comprimento 4, uma de comprimento 8 e outra de comprimento 16. O custo total é 2 + 4 + 8 + 16 = 30.

No longo prazo, essa eficiência pode ser computada pela adição em ordem reversa e usando uma série geométrica começando com um primeiro termo a1 = N e tendo uma razão comum de r = 1/2. A sum de uma série geométrica é dada por a_1 / (1-r) = 2N .

Isso é mais eficiente do que adicionar um caractere para criar uma nova sequência de comprimento 2, criando uma nova sequência de comprimento 3, 4, 5 e assim por diante, até 16. O algoritmo anterior usou esse processo para adicionar um único byte de cada vez. , e o custo total seria n (n + 1) / 2 = 16 (17) / 2 = 8 (17) = 136 .

Obviamente, 136 é um número muito maior que 30, e assim o algoritmo anterior leva muito, muito mais tempo para construir uma string.

Para comparar os dois methods, você pode ver quanto mais rápido o algoritmo recursivo (também chamado de "dividir e conquistar") está em uma cadeia de comprimento 123.457. No meu computador FreeBSD, este algoritmo, implementado na function stringFill3() , cria a string em 0.001058 segundos, enquanto a function stringFill1() cria a string em 0.0808 segundos. A nova function é 76 vezes mais rápida.

A diferença no desempenho aumenta à medida que o comprimento da string se torna maior. No limite, à medida que strings maiores e maiores são criadas, a function original se comporta mais ou menos como C1 (constante) vezes N^2 , e a nova function se comporta como C2 (constante) vezes N

Da nossa experiência, podemos determinar o valor de C1 para ser C1 = 0.0808 / (123457)2 = .00000000000530126997 , e o valor de C2 para ser C2 = 0.001058 / 123457 = .00000000856978543136 . Em 10 segundos, a nova function poderia criar uma string contendo 1,166,890,359 caracteres. Para criar essa mesma string, a function antiga precisaria de 7.218.384 segundos de tempo.

Isso é quase três meses em comparação com dez segundos!

Estou apenas respondendo (vários anos atrasado) porque minha solução original para esse problema está flutuando na Internet há mais de 10 anos e, aparentemente, ainda é pouco compreendida pelos poucos que se lembram disso. Eu pensei que escrevendo um artigo sobre isso aqui eu ajudaria:

Otimizações de desempenho para JavaScript / página de alta velocidade

Infelizmente, algumas das outras soluções apresentadas aqui ainda são algumas que levariam três meses para produzir a mesma quantidade de saída que uma solução adequada cria em 10 segundos.

Eu quero ter tempo para reproduzir parte do artigo aqui como uma resposta canônica no Stack Overflow.

Note que o algoritmo de melhor desempenho aqui é claramente baseado no meu algoritmo e provavelmente foi herdado da adaptação de terceira ou quarta gerações de outra pessoa. Infelizmente, as modificações resultaram na redução de seu desempenho. A variação da minha solução apresentada aqui talvez não compreendeu minha confusão for (;;) expressão for (;;) que se parece com o loop infinito principal de um servidor escrito em C, e que foi simplesmente projetado para permitir uma instrução de quebra cuidadosamente posicionada para controle de loop, a maneira mais compacta de evitar a replicação exponencial da string, um tempo extra desnecessário.

Este é bastante eficiente

 String.prototype.repeat = function(times){ var result=""; var pattern=this; while (times > 0) { if (times&1) result+=pattern; times>>=1; pattern+=pattern; } return result; }; 

Boas notícias! String.prototype.repeat é aceito para o Harmony (ECMAScript 6) .

 > "yo".repeat(2) "yoyo" 

O método está disponível em versões recentes do V8, usadas pelo Node.js, pelo Chrome ( String.repeat suportado desde a versão 41) e pelo Opera. As versões mais recentes do Safari e do Firefox também parecem ter suporte, mas o Internet Explorer não. Para obter uma lista atualizada, consulte MDN: String.prototype.repeat> Compatibilidade do navegador .

A MDN propõe o seguinte polyfill:

 if (!String.prototype.repeat) { String.prototype.repeat = function(count) { 'use strict'; if (this == null) { throw new TypeError('can\'t convert ' + this + ' to object'); } var str = '' + this; count = +count; if (count != count) { count = 0; } if (count < 0) { throw new RangeError('repeat count must be non-negative'); } if (count == Infinity) { throw new RangeError('repeat count must be less than infinity'); } count = Math.floor(count); if (str.length == 0 || count == 0) { return ''; } // Ensuring count is a 31-bit integer allows us to heavily optimize the // main part. But anyway, most current (August 2014) browsers can't handle // strings 1 << 28 chars or longer, so: if (str.length * count >= 1 < < 28) { throw new RangeError('repeat count must not overflow maximum string size'); } var rpt = ''; for (;;) { if ((count & 1) == 1) { rpt += str; } count >>>= 1; if (count == 0) { break; } str += str; } return rpt; } } 

Expandindo a solução de P.Bailey :

 String.prototype.repeat = function(num) { return new Array(isNaN(num)? 1 : ++num).join(this); } 

Desta forma, você deve estar a salvo de tipos de argumentos inesperados:

 var foo = 'bar'; alert(foo.repeat(3)); // Will work, "barbarbar" alert(foo.repeat('3')); // Same as above alert(foo.repeat(true)); // Same as foo.repeat(1) alert(foo.repeat(0)); // This and all the following return an empty alert(foo.repeat(false)); // string while not causing an exception alert(foo.repeat(null)); alert(foo.repeat(undefined)); alert(foo.repeat({})); // Object alert(foo.repeat(function () {})); // Function 

EDIT: Créditos para jerone para sua idéia ++num elegante ++num !

String.prototype.repeat é agora ES6 Standard.

 'abc'.repeat(3); //abcabcabc 

Use Array(N+1).join("string_to_repeat")

 /** @desc: repeat string @param: n - times @param: d - delimiter */ String.prototype.repeat = function (n, d) { return --n ? this + (d || '') + this.repeat(n, d) : '' + this }; 

isto é como repetir a string várias vezes usando o delimitador.

Aqui está uma melhoria de 5-7% na resposta do deficiente.

Desenrolar o loop, parando na count > 1 e executar um result += pattnern adicional result += pattnern concat result += pattnern após o loop. Isso evitará o pattern += pattern final pattern += pattern não utilizado antes do loop sem ter que usar um check if caro. O resultado final ficaria assim:

 String.prototype.repeat = function(count) { if (count < 1) return ''; var result = '', pattern = this.valueOf(); while (count > 1) { if (count & 1) result += pattern; count >>= 1, pattern += pattern; } result += pattern; return result; }; 

E aqui está o violino danificado para a versão desenrolada: http://jsfiddle.net/wsdfg/

 function repeat(s, n) { var r=""; for (var a=0;a 

Tests of the various methods:

 var repeatMethods = { control: function (n,s) { /* all of these lines are common to all methods */ if (n==0) return ''; if (n==1 || isNaN(n)) return s; return ''; }, divideAndConquer: function (n, s) { if (n==0) return ''; if (n==1 || isNaN(n)) return s; with(Math) { return arguments.callee(floor(n/2), s)+arguments.callee(ceil(n/2), s); } }, linearRecurse: function (n,s) { if (n==0) return ''; if (n==1 || isNaN(n)) return s; return s+arguments.callee(--n, s); }, newArray: function (n, s) { if (n==0) return ''; if (n==1 || isNaN(n)) return s; return (new Array(isNaN(n) ? 1 : ++n)).join(s); }, fillAndJoin: function (n, s) { if (n==0) return ''; if (n==1 || isNaN(n)) return s; var ret = []; for (var i=0; i0) { if (n&1) result+=s; n>>=1, s+=s; }; return result; } }; function testNum(len, dev) { with(Math) { return round(len+1+dev*(random()-0.5)); } } function testString(len, dev) { return (new Array(testNum(len, dev))).join(' '); } var testTime = 1000, tests = { biggie: { str: { len: 25, dev: 12 }, rep: {len: 200, dev: 50 } }, smalls: { str: { len: 5, dev: 5}, rep: { len: 5, dev: 5 } } }; var testCount = 0; var winnar = null; var inflight = 0; for (var methodName in repeatMethods) { var method = repeatMethods[methodName]; for (var testName in tests) { testCount++; var test = tests[testName]; var testId = methodName+':'+testName; var result = { id: testId, testParams: test } result.count=0; (function (result) { inflight++; setTimeout(function () { result.start = +new Date(); while ((new Date() - result.start) < testTime) { method(testNum(test.rep.len, test.rep.dev), testString(test.str.len, test.str.dev)); result.count++; } result.end = +new Date(); result.rate = 1000*result.count/(result.end-result.start) console.log(result); if (winnar === null || winnar.rate < result.rate) winnar = result; inflight--; if (inflight==0) { console.log('The winner: '); console.log(winnar); } }, (100+testTime)*testCount); }(result)); } } 

Here’s the JSLint safe version

 String.prototype.repeat = function (num) { var a = []; a.length = num < < 0 + 1; return a.join(this); }; 

For all browsers

This is about as concise as it gets :

 function repeat(s, n) { return new Array(n+1).join(s); } 

If you also care about performance, this is a much better approach :

 function repeat(s, n) { var a=[],i=0;for(;i 

If you want to compare the performance of both options, see this Fiddle and this Fiddle for benchmark tests. During my own tests, the second option was about 2 times faster in Firefox and about 4 times faster in Chrome!

For moderns browsers only :

In modern browsers, you can now also do this :

 function repeat(s,n) { return s.repeat(n) }; 

This option is not only shorter than both other options, but it's even faster than the second option.

Unfortunately, it doesn't work in any version of Internet explorer. The numbers in the table specify the first browser version that fully supports the method :

insira a descrição da imagem aqui

 function repeat(pattern, count) { for (var result = '';;) { if (count & 1) { result += pattern; } if (count >>= 1) { pattern += pattern; } else { return result; } } } 

You can test it at JSFiddle . Benchmarked against the hacky Array.join and mine is, roughly speaking, 10 (Chrome) to 100 (Safari) to 200 (Firefox) times faster (depending on the browser).

ES2015 has been realized this repeat() method!

http://www.ecma-international.org/ecma-262/6.0/#sec-string.prototype.repeat
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
http://www.w3schools.com/jsref/jsref_repeat.asp

 /** * str: String * count: Number */ const str = `hello repeat!\n`, count = 3; let resultString = str.repeat(count); console.log(`resultString = \n${resultString}`); /* resultString = hello repeat! hello repeat! hello repeat! */ ({ toString: () => 'abc', repeat: String.prototype.repeat }).repeat(2); // 'abcabc' (repeat() is a generic method) // Examples 'abc'.repeat(0); // '' 'abc'.repeat(1); // 'abc' 'abc'.repeat(2); // 'abcabc' 'abc'.repeat(3.5); // 'abcabcabc' (count will be converted to integer) // 'abc'.repeat(1/0); // RangeError // 'abc'.repeat(-1); // RangeError 

This may be the smallest recursive one:-

 String.prototype.repeat = function(n,s) { s = s || "" if(n>0) { s += this s = this.repeat(--n,s) } return s} 

Fiddle: http://jsfiddle.net/3Y9v2/

 function repeat(s, n){ return ((new Array(n+1)).join(s)); } alert(repeat('R', 10)); 

Simple recursive concatenation

I just wanted to give it a bash, and made this:

 function ditto( s, r, c ) { return c-- ? ditto( s, r += s, c ) : r; } ditto( "foo", "", 128 ); 

I can’t say I gave it much thought, and it probably shows 🙂

This is arguably better

 String.prototype.ditto = function( c ) { return --c ? this + this.ditto( c ) : this; }; "foo".ditto( 128 ); 

And it’s a lot like an answer already posted – I know this.

But why be recursive at all?

And how about a little default behaviour too?

 String.prototype.ditto = function() { var c = Number( arguments[ 0 ] ) || 2, r = this.valueOf(); while ( --c ) { r += this; } return r; } "foo".ditto(); 

Because , although the non recursive method will handle arbitrarily large repeats without hitting call stack limits, it’s a lot slower.

Why did I bother adding more methods that aren’t half as clever as those already posted?

Partly for my own amusement, and partly to point out in the simplest way I know that there are many ways to skin a cat, and depending on the situation, it’s quite possible that the apparently best method isn’t ideal.

A relatively fast and sophisticated method may effectively crash and burn under certain circumstances, whilst a slower, simpler method may get the job done – eventually.

Some methods may be little more than exploits, and as such prone to being fixed out of existence, and other methods may work beautifully in all conditions, but are so constructed that one simply has no idea how it works.

“So what if I dunno how it works?!”

A sério?

JavaScript suffers from one of its greatest strengths; it’s highly tolerant of bad behaviour, and so flexible it’ll bend over backwards to return results, when it might have been better for everyone if it’d snapped!

“With great power, comes great responsibility” 😉

But more seriously and importantly, although general questions like this do lead to awesomeness in the form of clever answers that if nothing else, expand one’s knowledge and horizons, in the end, the task at hand – the practical script that uses the resulting method – may require a little less, or a little more clever than is suggested.

These “perfect” algorithms are fun and all, but “one size fits all” will rarely if ever be better than tailor made.

This sermon was brought to you courtesy of a lack of sleep and a passing interest. Go forth and code!

Firstly, the OP’s questions seems to be about conciseness – which I understand to mean “simple and easy to read”, while most answers seem to be about efficiency – which is obviously not the same thing and also I think that unless you implement some very specific large data manipulating algorithms, shouldn’t worry you when you come to implement basic data manipulation Javascript functions. Conciseness is much more important.

Secondly, as André Laszlo noted, String.repeat is part of ECMAScript 6 and already available in several popular implementations – so the most concise implementation of String.repeat is not to implement it 😉

Lastly, if you need to support hosts that don’t offer the ECMAScript 6 implementation, MDN’s polyfill mentioned by André Laszlo is anything but concise.

So, without further ado – here is my concise polyfill:

 String.prototype.repeat = String.prototype.repeat || function(n){ return n< =1 ? this : this.concat(this.repeat(n-1)); } 

Yes, this is a recursion. I like recursions - they are simple and if done correctly are easy to understand. Regarding efficiency, if the language supports it they can be very efficient if written correctly.

From my tests, this method is ~60% faster than the Array.join approach. Although it obviously comes nowhere close disfated's implementation, it is much simpler than both.

My test setup is node v0.10, using "Strict mode" (I think it enables some sort of TCO ), calling repeat(1000) on a 10 character string a million times.

Use Lodash for Javascript utility functionality, like repeating strings.

Lodash provides nice performance and ECMAScript compatibility.

I highly recommend it for UI development and it works well server side, too.

Here’s how to repeat the string “yo” 2 times using Lodash:

 > _.repeat('yo', 2) "yoyo" 

Just another repeat function:

 function repeat(s, n) { var str = ''; for (var i = 0; i < n; i++) { str += s; } return str; } 

Recursive solution using divide and conquer:

 function repeat(n, s) { if (n==0) return ''; if (n==1 || isNaN(n)) return s; with(Math) { return repeat(floor(n/2), s)+repeat(ceil(n/2), s); } } 

I came here randomly and never had a reason to repeat a char in javascript before.

I was impressed by artistoex’s way of doing it and disfated’s results. I noticed that the last string concat was unnecessary, as Dennis also pointed out.

I noticed a few more things when playing with the sampling disfated put together.

The results varied a fair amount often favoring the last run and similar algorithms would often jockey for position. One of the things I changed was instead of using the JSLitmus generated count as the seed for the calls; as count was generated different for the various methods, I put in an index. This made the thing much more reliable. I then looked at ensuring that varying sized strings were passed to the functions. This prevented some of the variations I saw, where some algorithms did better at the single chars or smaller strings. However the top 3 methods all did well regardless of the string size.

Forked test set

http://jsfiddle.net/schmide/fCqp3/134/

 // repeated string var string = '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'; // count paremeter is changed on every test iteration, limit it's maximum value here var maxCount = 200; var n = 0; $.each(tests, function (name) { var fn = tests[name]; JSLitmus.test(++n + '. ' + name, function (count) { var index = 0; while (count--) { fn.call(string.slice(0, index % string.length), index % maxCount); index++; } }); if (fn.call('>', 10).length !== 10) $('body').prepend('

Error in "' + name + '"

'); }); JSLitmus.runAll();

I then included Dennis’ fix and decided to see if I could find a way to eek out a bit more.

Since javascript can’t really optimize things, the best way to improve performance is to manually avoid things. If I took the first 4 trivial results out of the loop, I could avoid 2-4 string stores and write the final store directly to the result.

 // final: growing pattern + prototypejs check (count < 1) 'final avoid': function (count) { if (!count) return ''; if (count == 1) return this.valueOf(); var pattern = this.valueOf(); if (count == 2) return pattern + pattern; if (count == 3) return pattern + pattern + pattern; var result; if (count & 1) result = pattern; else result = ''; count >>= 1; do { pattern += pattern; if (count & 1) result += pattern; count >>= 1; } while (count > 1); return result + pattern + pattern; } 

This resulted in a 1-2% improvement on average over Dennis’ fix. However, different runs and different browsers would show a fair enough variance that this extra code probably isn’t worth the effort over the 2 previous algorithms.

A chart

Edit: I did this mostly under chrome. Firefox and IE will often favor Dennis by a couple %.

Simple method:

 String.prototype.repeat = function(num) { num = parseInt(num); if (num < 0) return ''; return new Array(num + 1).join(this); } 

People overcomplicate this to a ridiculous extent or waste performance. Arrays? Recursion? You’ve got to be kidding me.

 function repeat (string, times) { var result = '' while (times-- > 0) result += string return result } 

Editar. I ran some simple tests to compare with the bitwise version posted by artistoex / disfated and a bunch of other people. The latter was only marginally faster, but orders of magnitude more memory-efficient. For 1000000 repeats of the word ‘blah’, the Node process went up to 46 megabytes with the simple concatenation algorithm (above), but only 5.5 megabytes with the logarithmic algorithm. The latter is definitely the way to go. Reposting it for the sake of clarity:

 function repeat (string, times) { var result = '' while (times > 0) { if (times & 1) result += string times >>= 1 string += string } return result } 

If you think all those prototype definitions, array creations, and join operations are overkill, just use a single line code where you need it. String S repeating N times:

 for (var i = 0, result = ''; i < N; i++) result += S; 

Concatenating strings based on an number.

 function concatStr(str, num) { var arr = []; //Construct an array for (var i = 0; i < num; i++) arr[i] = str; //Join all elements str = arr.join(''); return str; } console.log(concatStr("abc", 3)); 

Espero que ajude!