Crie uma sequência de comprimento variável, preenchida com um caractere repetido

164

Então, minha pergunta foi feita por outra pessoa em seu formulário Java aqui: Java - Crie uma nova instância String com comprimento especificado e preenchido com caracteres específicos. Melhor solução?

. . . mas estou procurando pelo seu equivalente em JavaScript.

Basicamente, pretendo preencher dinamicamente os campos de texto com caracteres "#", com base no atributo "comprimento máximo" de cada campo. Portanto, se uma entrada tiver maxlength="3", o campo será preenchido com "###".

Idealmente, haveria algo como o Java StringUtils.repeat("#", 10);, mas, até agora, a melhor opção em que posso pensar é percorrer e acrescentar os caracteres "#", um de cada vez, até que o comprimento máximo seja atingido. Não posso deixar de pensar que existe uma maneira mais eficiente de fazer isso do que isso.

Alguma ideia?

FYI - Não posso simplesmente definir um valor padrão na entrada, porque os caracteres "#" precisam ser claros no foco e, se o usuário não inseriu um valor, será necessário "reabastecer" no desfoque. É o passo de "reabastecimento" que me preocupa

talemyn
fonte
1
Você está fazendo isso para mascarar o texto de um campo de entrada?
Feisty Mango
@MatthewCox: Não, é mais uma exibição visual do comprimento máximo. Nesse caso, é para um conjunto de campos de número de telefone que são divididos nas três partes do número. Ele iria mostrar que os primeiros 2 campos precisa de 3 dígitos e os últimos necessidades 4.
talemyn
possível duplicado de Repeat String - Javascript
Felix Kling 2/13

Respostas:

302

A melhor maneira de fazer isso (que eu já vi) é

var str = new Array(len + 1).join( character );

Isso cria uma matriz com o comprimento especificado e, em seguida, une-a à sequência especificada para repetir. A .join()função respeita o comprimento da matriz, independentemente de os elementos terem valores atribuídos e valores indefinidos serem renderizados como cadeias vazias.

Você precisa adicionar 1 ao comprimento desejado porque a cadeia separadora fica entre os elementos da matriz.

Pontudo
fonte
Acertou em cheio. :) Eu só precisava lembrar de usar parseInt()o valor maxlength das entradas antes de usá-lo para dimensionar a matriz. É mesmo o que eu procurava . . . obrigado!
talemyn
11
Observe que esta solução é MUITO lenta. Parece sexy, mas provavelmente é exatamente o caminho errado para fazer isso.
Hogan
23
nova repeatfunção incorporada ao String.prototype lida com isso agora (ES6 +) #
AlexMA 18/16
Solução doente. Obrigado por isso!
C4d
156

Faça uma tentativa: P

s = '#'.repeat(10)

document.body.innerHTML = s


fonte
4
Definitivamente a implementação mais fácil, mas também a menos suportada, pois faz parte do ECMAScript 6. Parece que, no momento, é suportado apenas no Firefox, Chrome e na versão desktop do Safari.
talemyn
5
Se você não se importa em usar o lodash , pode usar o seguinte: _.repeat('*',10);
Geraud Gratacap 5/16/16
4
Agora é 2018 e o ES6 está bem estabelecido - essa deve ser a resposta aceita. E, no caso de precisar dar suporte a navegadores herdados para aqueles que não usam o Babel ou algo semelhante, você pode usar o lodash, como mencionado acima, ou um polyfill para backup.
precisa saber é o seguinte
@seanTcoyote Tanto quanto eu gostaria, ainda há pessoas que precisam lidar com o IE 11 e as visualizações da Web da Adroid, nenhuma das quais suporta o repeat()método. Ansioso pelo dia em que não preciso mais me preocupar com eles. . .
talemyn
Infelizmente, não é compatível com o IE
Lorenzo Lerate
30

Infelizmente, embora a abordagem Array.join mencionada aqui seja concisa, é cerca de 10 vezes mais lenta que uma implementação baseada em concatenação de cadeias. Ele executa especialmente mal em grandes seqüências de caracteres. Veja abaixo os detalhes completos de desempenho.

No Firefox, Chrome, Node.js.MOSOS, Node.js. Ubuntu e Safari, a implementação mais rápida que testei foi:

function repeatChar(count, ch) {
    if (count == 0) {
        return "";
    }
    var count2 = count / 2;
    var result = ch;

    // double the input until it is long enough.
    while (result.length <= count2) {
        result += result;
    }
    // use substring to hit the precise length target without
    // using extra memory
    return result + result.substring(0, count - result.length);
};

Isso é detalhado, portanto, se você quiser uma implementação concisa, poderá usar a abordagem ingênua; ele ainda executa entre 2 e 10 vezes melhor que a abordagem Array.join e também é mais rápido que a implementação duplicada para pequenas entradas. Código:

// naive approach: simply add the letters one by one
function repeatChar(count, ch) {
    var txt = "";
    for (var i = 0; i < count; i++) {
        txt += ch;
    }
    return txt;
}

Outras informações:

Zero Trick Pony
fonte
1
Isso ainda é MUITO mais lento (de 2 a 3 ordens de magnitude) do que minha solução.
Hogan
@Hogan Sua solução não cria uma string preenchida do nada. Você mesmo cria e depois extrai uma substring.
StanE 19/08/19
Essa abordagem é 10 vezes mais lenta que o Array.join no Node.js para cadeias extremamente grandes.
Anshul Koka
21

Eu criaria uma string constante e depois chamaria a substring.

Algo como

var hashStore = '########################################';

var Fiveup = hashStore.substring(0,5);

var Tenup = hashStore.substring(0,10);

Um pouco mais rápido também.

http://jsperf.com/const-vs-join

Hogan
fonte
1
Isso é muito inteligente! E ainda funciona com padrões de string contendo vários caracteres.
Markus9
1
Basta revisitar esta página porque parece que recebo muita atenção e queria comentar sua solução. Embora eu definitivamente goste da simplicidade e velocidade da sua abordagem, minha maior preocupação foi com a manutenção / reutilização. Enquanto em meu cenário específico, eu não estava precisando de mais de 4 caracteres, como uma função genérica, usar uma cadeia de comprimento fixo significa que você precisa voltar e atualizar a cadeia, se o seu caso de uso precisar de mais caracteres. As outras duas soluções permitem mais flexibilidade nesse sentido. 1 para a velocidade, no entanto.
talemyn
1
Ah, e, por outro lado (e eu percebo que isso é realmente mais uma preocupação estilística do que programática), manter uma sequência de mais de 30 caracteres que só será usada para 3-4 caracteres não se encaixava bem eu também . . . novamente, um problema menor relacionado à flexibilidade da solução para vários casos de uso.
talemyn
@talemyn Em TI, tudo é uma troca. Tenha uma sequência de 4 caracteres e aumente ou tenha uma sequência de 30 caracteres e não se preocupe. Em suma, uma cadeia constante de qualquer tamanho - até 1k - é um uso minúsculo de recursos em máquinas modernas.
Hogan
@ Hogan - concordo totalmente. . . e, na verdade, isso é parte do motivo de eu também não enfatizar muito a velocidade. No meu caso de uso específico, eu procurava três cadeias de caracteres com 3, 3 e 4 caracteres. Embora a solução de Pointy fosse mais lenta, uma vez que as cordas eram curtas e havia apenas algumas delas, fui com a dele porque gostei do fato de ser simplificada, fácil de ler e de manter. No final, todas são boas soluções para diferentes variações da questão. . . tudo vai depender de quais são as prioridades para sua situação específica.
talemyn
5

Uma ótima opção do ES6 seria padStartuma string vazia. Como isso:

var str = ''.padStart(10, "#");

Nota: isso não funcionará no IE (sem um polyfill).

Mendy
fonte
3
Alguma razão em particular para você recomendar essa abordagem ES6 em detrimento da outra ( s = '#'.repeat(10)) da resposta de @ user4227915 acima?
talemyn
@talemyn Sintaxe mais simples
Mendy
3

Versão que funciona em todos os navegadores

Esta função faz o que você deseja e executa muito mais rapidamente do que a opção sugerida na resposta aceita:

var repeat = function(str, count) {
    var array = [];
    for(var i = 0; i <= count;)
        array[i++] = str;
    return array.join('');
}

Você usa assim:

var repeatedCharacter = repeat("a", 10);

Para comparar o desempenho desta função com o da opção proposta na resposta aceita, consulte este Fiddle e este Fiddle para obter referências.

Versão apenas para navegadores modernos

Nos navegadores modernos, agora você também pode fazer isso:

var repeatedCharacter = "a".repeat(10) };

Esta opção é ainda mais rápida. No entanto, infelizmente, ele não funciona em nenhuma versão do Internet Explorer.

Os números na tabela especificam a primeira versão do navegador que suporta totalmente o método:

insira a descrição da imagem aqui

John Slegers
fonte
3
For Evergreen browsers, this will build a staircase based on an incoming character and the number of stairs to build.
function StairCase(character, input) {
    let i = 0;
    while (i < input) {
        const spaces = " ".repeat(input - (i+1));
        const hashes = character.repeat(i + 1);
        console.log(spaces + hashes);
        i++;
    }
}

//Implement
//Refresh the console
console.clear();
StairCase("#",6);   

Você também pode adicionar um polyfill para Repeat para navegadores mais antigos

    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;
        }
        // Could we try:
        // return Array(count + 1).join(this);
        return rpt;
      }
    } 
Terry Slack
fonte
2

Com base nas respostas de Hogan e Zero Trick Pony. Eu acho que isso deve ser rápido e flexível o suficiente para lidar bem com a maioria dos casos de uso:

var hash = '####################################################################'

function build_string(length) {  
    if (length == 0) {  
        return ''  
    } else if (hash.length <= length) {  
        return hash.substring(0, length)  
    } else {  
        var result = hash  
        const half_length = length / 2  
        while (result.length <= half_length) {  
            result += result  
        }  
        return result + result.substring(0, length - result.length)  
    }  
}  
Leandro
fonte
Adicione mais algumas explicações à sua resposta, pois um trecho de código em si não fornece uma resposta.
Clijsters
@Leandro Inteligente e minimiza o processamento! +1
LMSingh
Eu acho que você quis hash.length> = comprimento
cipak
1

Você pode usar a primeira linha da função como uma linha, se desejar:

function repeat(str, len) {
    while (str.length < len) str += str.substr(0, len-str.length);
    return str;
}
Salsaparrilha
fonte