Cree una cadena de longitud variable, llena con un carácter repetido

164

Entonces, mi pregunta ha sido formulada por otra persona en su forma de Java aquí: Java: crea una nueva instancia de String con una longitud especificada y llena con un carácter específico. ¿Mejor solución?

. . . pero estoy buscando su equivalente de JavaScript.

Básicamente, quiero llenar dinámicamente los campos de texto con caracteres "#", según el atributo "maxlength" de cada campo. Entonces, si una entrada tiene maxlength="3", el campo se rellenará con "###".

Idealmente, habría algo como el Java StringUtils.repeat("#", 10); , pero, hasta ahora, la mejor opción que se me ocurre es recorrer y agregar los caracteres "#", uno a la vez, hasta alcanzar la longitud máxima. No puedo evitar la sensación de que hay una manera más eficiente de hacerlo que eso.

¿Algunas ideas?

FYI: no puedo simplemente establecer un valor predeterminado en la entrada, porque los caracteres "#" deben borrarse al enfocar y, si el usuario no ingresó un valor, deberán "rellenarse" en el desenfoque. Es el paso de "recarga" que me preocupa

talemyn
fuente
1
¿Estás haciendo esto para enmascarar el texto de un campo de entrada?
Feisty Mango
@MatthewCox: No, es más para proporcionar una visualización de la longitud máxima. En este caso, es para un conjunto de campos de números de teléfono que se dividen en las 3 partes del número.
Mostraría
posible duplicado de Repeat String - Javascript
Felix Kling

Respuestas:

302

La mejor manera de hacer esto (que he visto) es

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

Eso crea una matriz con la longitud dada, y luego la une con la cadena dada para repetir. La .join()función respeta la longitud de la matriz independientemente de si los elementos tienen valores asignados y los valores indefinidos se representan como cadenas vacías.

Debe agregar 1 a la longitud deseada porque la cadena de separación va entre los elementos de la matriz.

Puntiagudo
fuente
Justo en el clavo. :) Solo necesitaba recordar usar parseInt()el valor de longitud máxima de las entradas antes de usarlo para dimensionar la matriz. Justo lo que estaba buscando . . . ¡Gracias!
talemyn
11
Tenga en cuenta que esta solución es MUY lenta. Se ve sexy, pero probablemente sea exactamente la forma incorrecta de hacer esto.
Hogan
23
nueva repeatfunción integrada en String.prototype maneja esto ahora (ES6 +)
AlexMA
Solución enferma. ¡Gracias por eso!
C4d
156

Prueba esto: P

s = '#'.repeat(10)

document.body.innerHTML = s


fuente
44
Definitivamente, la implementación más fácil, pero también la menos compatible, ya que es parte de ECMAScript 6. Parece que, en este momento, solo es compatible con Firefox, Chrome y la versión de escritorio de Safari.
talemyn
55
Si no le importa usar lodash , puede usar lo siguiente: _.repeat('*',10);
Geraud Gratacap
44
Ahora es 2018 y ES6 está bien establecido: esta debería ser la respuesta aceptada. Y en caso de que necesite admitir navegadores heredados para aquellos que no usan Babel o algo similar, puede usar lodash como se mencionó anteriormente, o un polyfill para respaldo.
seanTcoyote
@seanTcoyote Por mucho que quisiera, todavía hay personas que tienen que lidiar con IE 11 y las Webviews de Adroid, ninguna de las cuales admite el repeat()método. Estoy deseando que llegue el día en que ya no tenga que preocuparme por ellos. . .
talemyn
Desafortunadamente, no es compatible con IE
Lorenzo Lerate
30

Desafortunadamente, aunque el enfoque Array.join mencionado aquí es conciso, es aproximadamente 10 veces más lento que una implementación basada en concatenación de cadenas. Se desempeña especialmente mal en cuerdas grandes. Vea a continuación los detalles completos del rendimiento.

En Firefox, Chrome, Node.js MacOS, Node.js Ubuntu y Safari, la implementación más rápida que probé fue:

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);
};

Esto es detallado, por lo que si desea una implementación concisa podría optar por el enfoque ingenuo; todavía funciona entre 2X y 10X mejor que el enfoque Array.join, y también es más rápido que la implementación de duplicación para entradas pequeñas. 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;
}

Más información:

Poni Cero Truco
fuente
1
Esto sigue siendo MUCHO más lento (de 2 a 3 órdenes de magnitud) que mi solución.
Hogan
@Hogan Su solución no crea una cadena llena de la nada. Lo crea usted mismo y luego simplemente extrae una subcadena.
StanE
Este enfoque es 10 veces más lento que Array.join en Node.js para cadenas extremadamente grandes.
Anshul Koka
21

Crearía una cadena constante y luego llamaría a la subcadena.

Algo como

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

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

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

Un poco más rápido también.

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

Hogan
fuente
1
¡Esto es muy inteligente! E incluso funciona con patrones de cadena que contienen múltiples caracteres.
Markus
1
Solo vuelvo a visitar esta página porque parece seguir recibiendo mucha atención y quería comentar sobre su solución. Si bien definitivamente me gusta la simplicidad y la velocidad de su enfoque, mi mayor preocupación fue el mantenimiento / reutilización. Mientras que en mi escenario específico, no necesitaba más de 4 caracteres, como una función genérica, el uso de una cadena de longitud fija significa que debe volver y actualizar la cadena, si su caso de uso alguna vez necesita más caracteres. Las otras dos soluciones permiten una mayor flexibilidad en ese sentido. Sin embargo, +1 para la velocidad.
talemyn
1
Ah, y por otro lado (y me doy cuenta de que esto es realmente más una preocupación estilística, que programática), mantener una cadena de más de 30 caracteres que solo se usará para 3-4 caracteres no encajaba bien con yo tampoco . . . de nuevo, un problema menor en torno a la flexibilidad de la solución para varios casos de uso.
talemyn
@talemyn En TI todo es una compensación. Tener una cadena de 4 caracteres y hacerla más grande o tener una cadena de 30 caracteres y no te preocupes. Con todo, una cadena constante de cualquier tamaño, incluso 1k, es un uso minúsculo de recursos en máquinas modernas.
Hogan
@Hogan: totalmente de acuerdo. . . y, esa es en realidad parte de la razón por la que tampoco puse un gran énfasis en la velocidad. En mi caso de uso específico, estaba buscando tres cadenas de 3, 3 y 4 caracteres de longitud. Si bien la solución de Pointy fue más lenta, ya que las cadenas eran cortas y solo había unas pocas, fui con la suya porque me gustó el hecho de que era simplificada, fácil de leer y fácil de mantener. Al final, todas son buenas soluciones para diferentes variaciones de la pregunta. . . todo dependerá de cuáles sean las prioridades para su situación específica.
talemyn
5

Una gran opción de ES6 sería padStartuna cadena vacía. Me gusta esto:

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

Nota: esto no funcionará en IE (sin un polyfill).

Mendy
fuente
3
¿Alguna razón en particular por la que recomendaría este enfoque ES6 sobre el otro ( s = '#'.repeat(10)) de la respuesta de @ user4227915 anterior?
talemyn
@talemyn Sintaxis más simple
Mendy
3

Versión que funciona en todos los navegadores.

Esta función hace lo que desea y funciona mucho más rápido que la opción sugerida en la respuesta aceptada:

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

Lo usas así:

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

Para comparar el rendimiento de esta función con el de la opción propuesta en la respuesta aceptada, consulte este violín y este violín para obtener puntos de referencia.

Versión solo para navegadores modernos

En los navegadores modernos, ahora también puede hacer esto:

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

Esta opción es aún más rápida. Sin embargo, desafortunadamente no funciona en ninguna versión de Internet Explorer.

Los números en la tabla especifican la primera versión del navegador que soporta totalmente este método :

ingrese la descripción de la imagen aquí

John Slegers
fuente
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);   

También puede agregar un polyfill para Repeat para navegadores antiguos

    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
fuente
2

Basado en las respuestas de Hogan y Zero Trick Pony. Creo que esto debería ser lo suficientemente rápido y flexible como para manejar bien la mayoría de los 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
fuente
Agregue algunas explicaciones más a su respuesta, ya que un fragmento de código en sí no proporciona una respuesta.
Clijsters
@Leandro Clever y minimiza el procesamiento! +1
LMSingh
Creo que te referías a hash.length> = length
cipak
1

Puede usar la primera línea de la función como una línea si desea:

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