Répéter Chaîne De Caractères - Javascript

Quelle est la meilleure méthode ou la plus concise pour retourner une chaîne répétée une quantité arbitraire de fois?

ce qui suit est mon meilleur tir jusqu'à présent:

function repeat(s, n){
    var a = [];
    while(a.length < n){
        a.push(s);
    }
    return a.join('');
}
262
demandé sur brad 2008-10-15 00:10:18

28 réponses

Note aux nouveaux lecteurs: cette réponse est ancienne et et pas terriblement pratique - il est juste "intelligent" parce qu'il utilise des choses de réseau pour obtenir Chaîne de choses à faire. Quand j'ai écrit "moins de processus" je voulais vraiment dire "moins de code" parce que, comme d'autres l'ont fait remarquer dans des réponses subséquentes, fonctionne comme un cochon. Alors ne l'utilisez pas si la vitesse vous importe.

je mettrais cette fonction directement sur L'objet String. Plutôt créer un tableau, le remplir, et le joindre avec un char vide, juste créer un tableau de la bonne longueur, et le joindre avec votre chaîne désirée. Même résultat, moins de!

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

alert( "string to repeat\n".repeat( 4 ) );
394
répondu Peter Bailey 2015-06-07 17:24:33

j'ai testé la performance de toutes les approches proposées.

Voici la variante la plus rapide j'ai.

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 stand-alone de la fonction:

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

il est basé sur artistoex algorithme. Il est vraiment rapide. Et plus grand le count , plus vite il va par rapport à la traditionnelle new Array(count + 1).join(string) approche.

Je n'ai changé que 2 choses:

  1. remplacé pattern = this par pattern = this.valueOf() (efface une conversion de type évidente);
  2. , a ajouté if (count < 1) case de prototypejs en haut de la fonction à exclure les actions inutiles dans ce cas.
  3. appliquée optimisation de Dennis réponse (5-7% de la vitesse jusqu')

UPD

a créé un petit terrain de jeu de performance-test ici pour ceux qui sont intéressés.

variable count ~ 0 .. 100:

tester la performance de différentes variantes de chaîne.repeat() http://tinyurl.com/kb3raxr

constante count = 1024:

tester la performance de différentes variantes de chaîne.repeat() http://tinyurl.com/k527auo

utilisez-le et faites-le encore plus rapide si vous pouvez:)

197
répondu disfated 2017-05-23 11:33:24

ce problème est un problème d'optimisation bien connu /" classique "pour JavaScript, causé par le fait que les chaînes JavaScript sont" immuables " et l'ajout par concaténation même d'un seul caractère à une chaîne nécessite la création de, y compris l'allocation de mémoire pour et la copie à, une nouvelle chaîne entière.

malheureusement, la réponse acceptée sur cette page est fausse, où "faux" signifie par un facteur de performance de 3x pour les chaînes simples d'un caractère, et 8x-97x pour des cordes courtes répétées plus de fois, à 300x pour répéter des phrases, et infiniment faux en prenant la limite des rapports de complexité des algorithmes comme n va à l'infini. En outre, il ya une autre réponse sur cette page qui est presque juste (basé sur l'une des nombreuses générations et variations de la solution correcte circulant à travers L'Internet au cours des 13 dernières années). Cependant, ce "presque" solution manque un point-clé de l'algorithme correct provoquant une 50% dégradation des performances.

JS Résultats pour la accepté de répondre, le plus performant de réponse (basé sur une version dégradée de l'algorithme original de cette réponse), et cette réponse à l'aide de mon algorithme créé il y a 13 ans

~ octobre 2000, j'ai publié un algorithme pour exacte de ce problème qui a été largement adapté, modifié, puis finalement mal compris et oublié. Pour remédier à ce problème, en Août, 2008 j'ai publié un article http://www.webreference.com/programming/javascript/jkm3/3.html expliquant l'algorithme et l'utilisant comme un exemple simple d'optimisation JavaScript universel. À ce jour, Web Reference a effacé mes coordonnées et même mon nom de cet article. Et encore une fois, l'algorithme a été largement adapté, modifié, puis mal compris et largement oublié.

chaîne d'Origine répétition/multiplication JavaScript par l'algorithme Joseph Myers, circa Y2K comme fonction de multiplication de texte dans le texte.js; publié en août 2008 sous cette forme par référence Web: http://www.webreference.com/programming/javascript/jkm3/3.html (la article Utilisé la fonction comme un exemple D'optimisation JavaScript, ce qui est le seul pour l'étrange nom " 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;
}

Dans les deux mois qui ont suivi la publication de cet article, cette même question a été postée à Stack Overflow et a volé sous mon radar jusqu'à maintenant, alors qu'apparemment l'algorithme original pour ce problème a de nouveau été oublié. La meilleure solution disponible sur cette page de débordement de pile est une version modifiée de ma solution, éventuellement séparée par plusieurs générations. Malheureusement, les modifications ont ruiné l'optimalité de la solution. En fait, en changeant la structure de la boucle de mon original, le la solution modifiée effectue une étape supplémentaire complètement inutile de duplication exponentielle (en joignant ainsi la plus grande chaîne utilisée dans la bonne réponse avec elle-même un temps supplémentaire et en la rejetant).

ci-dessous suit une discussion de quelques optimisations JavaScript liées à toutes les réponses à ce problème et pour le bénéfice de tous.

Technique: éviter les références aux objets ou aux propriétés des objets

pour illustrer comment cette technique fonctionne, nous utilisons une fonction JavaScript de la vie réelle qui crée des chaînes de n'importe quelle longueur est nécessaire. Et comme nous le verrons, d'autres optimisations peuvent être ajoutées!

une fonction semblable à celle utilisée ici est de créer un remplissage pour aligner des colonnes de texte, pour formater de l'argent, ou pour remplir des données de bloc jusqu'à la limite. Une fonction de génération de texte permet également l'entrée de longueur variable pour tester toute autre fonction qui opère sur le texte. Cette fonction est l'une des plus importantes composants du module de traitement de texte JavaScript.

en continuant, nous couvrirons deux autres des techniques d'optimisation les plus importantes tout en développant le code original en un algorithme optimisé pour créer des chaînes. Le résultat final est une fonction de haute performance et de puissance industrielle que j'ai utilisée partout-alignant les prix des articles et les totaux dans les formulaires de commande JavaScript, le formatage des données et le formatage des courriels et des messages textes et de nombreuses autres utilisations.

code Original pour créer des chaînes stringFill1() 1519960920"

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

La syntaxe est ici clairement. Comme vous pouvez le voir, nous avons déjà utilisé des variables de fonction locales, avant de passer à d'autres optimisations.

soyez conscient qu'il y a une référence innocente à un objet s.length dans le code qui nuit à son exécution. Pire encore, l'utilisation de cette propriété de l'objet réduit la simplicité du programme en faisant l'hypothèse que le lecteur connaît les propriétés des objets JavaScript string.

l'utilisation de cette propriété d'objet détruit la généralité du programme d'ordinateur. Le programme suppose que x doit être une chaîne de longueur un. Cela limite l'application de la fonction stringFill1() à tout sauf la répétition de caractères simples. Même les caractères simples ne peuvent pas être utilisés s'ils contiennent plusieurs octets comme l'entité HTML &nbsp; .

le pire problème causé par cette utilisation inutile d'une propriété d'objet est que la fonction crée une boucle infinie si elle est testée sur une chaîne de caractères vide x . Pour vérifier la généralité, appliquer un programme à la plus petite quantité possible d'entrée. Un programme qui s'écrase lorsqu'on lui demande de dépasser la quantité de mémoire disponible a une excuse. Un programme comme celui-ci qui s'écrase lorsqu'on lui demande de ne rien produire est inacceptable. Parfois le joli code est un code toxique.

la simplicité peut être un objectif ambigu de la programmation informatique, mais en général ce n'est pas le cas. Lorsqu'un programme manque de généralité raisonnable, il n'est pas valable de dire: "le programme est assez bon dans la mesure où il va."Comme vous pouvez le voir, l'utilisation de la propriété string.length empêche ce programme de fonctionner dans un cadre général, et en fait, le programme incorrect est prêt à causer un crash du navigateur ou du système.

Est-il un moyen d'améliorer la les performances de ce JavaScript ainsi que de prendre soin de ces deux graves problèmes?

bien sûr. Utilisez des entiers.

code optimisé pour la création de chaînes stringFill2()

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

code de synchronisation pour comparer stringFill1() et 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); 

Le succès de stringFill2()

stringFill1() prend 47.297 microsecondes (millionièmes de seconde) pour remplir une chaîne de 100 octets, et stringFill2() prend 27.68 microsecondes pour faire la même chose. C'est presque un doublement de la performance en évitant une référence à une propriété d'objet.

Technique: éviter d'ajouter des cordes courtes aux cordes longues

notre résultat précédent était bon--très bon, en fait. La fonction améliorée stringFill2() est beaucoup plus rapide en raison de l'utilisation de nos deux premières optimisations. Croiriez-vous si je vous disais qu'il peut être amélioré pour être plusieurs fois plus rapide qu'elle ne l'est maintenant?

Oui, nous pouvons atteindre cet objectif. Maintenant, nous devons expliquer comment nous évitez d'ajouter des chaînes courtes à longues chaînes.

le comportement à court terme semble être assez bon, en comparaison de notre fonction originale. Les informaticiens aiment analyser le "comportement asymptotique" d'une fonction ou d'un ordinateur algorithme de programme, ce qui signifie d'étudier son comportement à long terme en le testant avec des entrées plus grandes. Parfois, sans faire d'autres tests, on ne se rend jamais compte de la façon dont un programme informatique pourrait être amélioré. Pour voir ce qui va se passer, nous allons créer une chaîne de 200 octets.

le problème qui apparaît avec stringFill2()

en utilisant notre fonction de synchronisation, nous trouvons que le temps passe à 62.54 microsecondes pour un Chaîne de 200 octets, contre 27,68 pour une chaîne de 100 octets. Il semble que le temps devrait être doublé pour faire deux fois plus de travail, mais à la place il est triplé ou quadruplé. D'après l'expérience de la programmation, ce résultat semble étrange, parce que la fonction devrait être un peu plus rapide puisque le travail est fait plus efficacement (200 octets par appel de fonction plutôt que 100 octets par appel de fonction). Ce problème concerne une propriété insidieuse des chaînes JavaScript: les chaînes JavaScript sont "immuable."

immuable signifie que vous ne pouvez pas changer une chaîne une fois qu'elle est créée. En ajoutant un byte à la fois, nous n'épuisons pas un byte de plus. On est en train de recréer la chaîne entière plus un octet de plus.

en effet, pour ajouter un octet de plus à une chaîne de 100 octets, il faut 101 octets de travail. Analysons brièvement le coût de calcul pour créer une chaîne de N octets. Le coût de l'ajout du premier octet est 1 l'unité d'effort de calcul. Le coût de l'ajout du second octet n'est pas une unité mais 2 unités (copier le premier octet vers un nouvel objet string ainsi que l'ajout du second octet). Le troisième octet nécessite un coût de 3 unités, etc.

C(N) = 1 + 2 + 3 + ... + N = N(N+1)/2 = O(N^2) . Le symbole O(N^2) se prononce Big O de N au carré, et signifie que le coût de calcul à long terme est proportionnel au carré de la longueur de la corde. Pour créer 100 caractères prend 10.000 unités de travail, et à créer 200 caractères nécessite 40 000 unités de travail.

c'est pourquoi il a fallu plus de deux fois plus de temps pour créer 200 caractères que 100 caractères. En fait, il devrait prendre quatre fois plus longtemps. Notre expérience de programmation a été correcte en ce que le travail est fait un peu plus efficacement pour des cordes plus longues, et donc il a fallu seulement environ trois fois plus de temps. Une fois que le overhead de l'appel de fonction devient négligeable quant à la longueur d'une chaîne que nous créons, il sera en fait, il faut quatre fois plus de temps pour créer une chaîne deux fois plus longue.

(note historique: cette analyse ne s'applique pas nécessairement aux chaînes en code source, comme html = 'abcd\n' + 'efgh\n' + ... + 'xyz.\n' , car le compilateur de code source JavaScript peut assembler les chaînes avant de les transformer en un objet de chaîne JavaScript. Il y a quelques années à peine, L'implémentation KJS de JavaScript s'est figée ou s'est écrasée lors du chargement de longues chaînes de code source jointes par des signes plus. Depuis le le temps de calcul était O(N^2) il n'était pas difficile de faire des pages web qui ont surchargé le navigateur Web Konqueror ou Safari, qui a utilisé le noyau du moteur JavaScript KJSCRIPT. Je suis tombé sur ce problème pour la première fois lorsque je développais un langage de balisage et JavaScript markup language parser, puis j'ai découvert ce qui causait le problème lorsque j'ai écrit mon script pour JavaScript Includes.)

il est clair que cette dégradation rapide des performances est un énorme problème. Comment pouvons-nous traiter avec cela, étant donné que nous ne pouvons pas changer la façon dont JavaScript gère les chaînes comme des objets immuables? La solution est d'utiliser un algorithme qui recrée la chaîne aussi peu de fois que possible.

pour clarifier, notre but est d'éviter d'ajouter des chaînes courtes à des chaînes longues, puisque pour ajouter la chaîne courte, toute la chaîne longue doit aussi être dupliquée.

comment l'algorithme fonctionne pour éviter d'ajouter des chaînes courtes à des chaînes longues

Voici un bon moyen de réduire le nombre de fois que de nouveaux objets string sont créés. Concaténer plus la longueur de la chaîne, de sorte que plus d'un octet à la fois est ajouté à la sortie.

par exemple, pour faire une chaîne de longueur 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 */

pour ce faire, il a fallu créer une chaîne de longueur 1, Créer une chaîne de longueur 2, Créer une chaîne de longueur 4, Créer une chaîne de longueur 8, et enfin, créer une chaîne de longueur de 9. Coût combien avons-nous sauvés?

Vieille coût C(9) = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 9 = 45 .

Nouvelle formule C(9) = 1 + 2 + 4 + 8 + 9 = 24 .

Notez que nous avons dû ajouter une chaîne de longueur 1 à une chaîne de longueur 0, puis une chaîne de longueur 1 à une chaîne de longueur 1, puis une chaîne de longueur 2 pour une chaîne de longueur 2, puis une chaîne de longueur 4 pour une chaîne de longueur 4, puis une chaîne de longueur 8 pour une chaîne de longueur 1, afin d'obtenir une chaîne de caractères de longueur 9. Quel en résumé, nous évitons d'ajouter des cordes courtes à de longues cordes, ou en d'autres termes, nous essayons de concaténer des cordes qui sont de longueur égale ou presque égale.

Pour l'ancien coût de calcul, nous avons trouvé une formule N(N+1)/2 . Est-il une formule pour le nouveau prix? Oui, mais c'est compliqué. La chose importante est qu'il est O(N) , et donc doubler la longueur de la corde sera environ le double de la quantité de travail plutôt que de le quadrupler.

le code qui met en œuvre cette nouvelle idée est presque aussi compliqué que la formule pour le coût de calcul. Quand vous le lisez, rappelez-vous que >>= 1 signifie se déplacer à droite par 1 octet. Donc si n = 10011 est un nombre binaire, alors n >>= 1 donne la valeur n = 1001 .

l'autre partie du code que vous pourriez ne pas reconnaître est le bitwise and operator, écrit & . L'expression n & 1 évalue vrai si le dernier chiffre binaire de n est 1, et false si le dernier chiffre binaire de n est de 0.

hautement efficace stringFill3() fonction

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

il semble laid à l'oeil non formé, mais sa performance est rien moins que belle.

voyons à quel point cette fonction fonctionne bien. Après avoir vu les résultats, il est probable que vous n'oublierez jamais la différence entre un Algorithme O(N^2) et algorithme O(N) .

stringFill1() prend 88.7 microsecondes (millionièmes de seconde) pour créer une chaîne de 200 octets, stringFill2() prend 62.54, et stringFill3() ne prend que 4.608. Ce que fait cet algorithme tellement mieux? Toutes les fonctions ont profité de l'utilisation de variables locales de fonction, mais en profitant de la deuxième et troisième techniques d'optimisation a ajouté une amélioration de vingt fois à la performance de stringFill3() .

analyse approfondie

Qu'est-ce qui fait que cette fonction particulière souffle la concurrence hors de l'eau?

comme je l'ai mentionné, la raison pour laquelle ces deux fonctions, stringFill1() et stringFill2() , fonctionnent si lentement est que les chaînes JavaScript sont immuables. La mémoire ne peut pas être réattribuée pour permettre qu'un octet de plus à la fois soit ajouté aux données de chaîne stockées par JavaScript. Chaque fois un octet de plus est ajouté à la fin de la chaîne, la chaîne entière est régénéré à partir de début à la fin.

ainsi, pour améliorer la performance du script, il faut précalculer des chaînes de longueur plus longue en concaténant deux chaînes ensemble à l'avance, puis en construisant récursivement la longueur de chaîne désirée.

par exemple, pour créer une chaîne de 16 octets, une chaîne de deux octets serait d'abord précomputée. Alors la chaîne de deux octets serait réutilisée pour précalculer une chaîne de quatre octets. Alors la chaîne de quatre octets serait réutilisée pour précalculer une chaîne de huit octets. Enfin, deux chaînes de huit octets seraient réutilisées pour créer la nouvelle chaîne désirée de 16 octets. Dans l'ensemble, quatre nouvelles chaînes ont dû être créés, l'un de longueur 2, une de 4, une de 8 et une de 16. Le coût total est de 2 + 4 + 8 + 16 = 30.

À long terme, cette efficacité peut être calculé en ajoutant dans l'ordre inverse et en utilisant une série géométrique en commençant par un premier terme a1 = N et ayant un rapport commun de r = 1/2. La somme d'une série géométrique est donnée par la a_1 / (1-r) = 2N .

c'est plus efficace que d'ajouter un caractère pour créer une nouvelle chaîne de longueur 2, créant une nouvelle chaîne de longueur 3, 4, 5, et ainsi de suite, jusqu'à 16. L'algorithme précédent utilisait ce processus d'ajout d'un seul octet à la fois, et le coût total de celui-ci serait n (n + 1) / 2 = 16 (17) / 2 = 8 (17) = 136 .

évidemment, 136 est un bien plus grand nombre de 30, et donc l'algorithme précédent prend beaucoup, beaucoup plus de temps pour construire une chaîne de caractères.

pour comparer les deux méthodes, vous pouvez voir à quelle vitesse l'algorithme récursif (aussi appelé" diviser pour mieux régner") est sur une chaîne de longueur 123,457. Sur mon ordinateur FreeBSD, cet algorithme, implémenté dans la fonction stringFill3() , crée la chaîne en 0.001058 secondes, tandis que la fonction stringFill1() originale crée la chaîne en 0.0808 secondes. La nouvelle fonction est 76 fois plus rapide.

la différence de performance augmente avec la longueur de la corde. Dans la limite où des chaînes de plus en plus grandes sont créées, la fonction originale se comporte à peu près comme C1 (constante) fois N^2 , et la nouvelle fonction se comporte comme C2 (constante) fois N .

de notre expérience, nous pouvons déterminer la valeur de C1 pour être C1 = 0.0808 / (123457)2 = .00000000000530126997 , et la valeur de C2 être C2 = 0.001058 / 123457 = .00000000856978543136 . En 10 secondes, la nouvelle fonction pourrait créer une chaîne contenant 1,166,890,359 caractères. Afin de créer cette même chaîne, l'ancienne fonction aurait besoin 7,218,384 secondes de temps.

c'est presque trois mois comparé à dix secondes!

je réponds seulement (plusieurs années de retard) parce que ma solution originale à ce problème a été flottant autour de L'Internet depuis plus de 10 ans, et est apparemment toujours mal compris par les quelques-uns qui en souviens. J'ai pensé qu'en écrivant un article à ce sujet ici, je vous aiderais:

optimisations de la Performance pour la Haute Vitesse JavaScript / Page 3

malheureusement, certaines des autres solutions présentées ici sont encore quelques-unes de celles qui mettraient trois mois à produire la même quantité de sortie qu'une solution appropriée crée en 10 Secondes.

je veux prenez le temps de reproduire ici une partie de l'article comme réponse canonique sur le débordement de la pile.

notez que l'algorithme le plus performant ici est clairement basé sur mon algorithme et a probablement été hérité de l'adaptation de 3ème ou 4ème génération de quelqu'un d'autre. Malheureusement, les modifications ont entraîné une réduction de son rendement. La variation de ma solution présentée ici n'a peut-être pas compris mon expression confuse for (;;) qui ressemble à l'infini principal boucle d'un serveur écrit en C, et qui a été simplement conçu pour permettre une instruction de coupure soigneusement positionnée pour le contrôle de boucle, la manière la plus compacte d'éviter la répétition exponentielle de la chaîne un temps supplémentaire inutile.

43
répondu Joseph Myers 2013-07-30 15:56:06

celui-ci est assez efficace

String.prototype.repeat = function(times){
    var result="";
    var pattern=this;
    while (times > 0) {
        if (times&1)
            result+=pattern;
        times>>=1;
        pattern+=pattern;
    }
    return result;
};
39
répondu artistoex 2010-11-11 08:39:17

bonne nouvelle! String.prototype.repeat est accepté pour l'Harmonie (ECMAScript 6) .

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

la méthode est disponible dans les versions récentes de V8, utilisé par noeud.js, Chrome ( String.repeat supporté depuis la version 41) et Opera. Les nouvelles versions de Safari et de Firefox semblent aussi avoir un support, mais Internet Explorer ne l'a pas. Pour une liste à jour, voir MDN: String.prototype.répéter > compatibilité avec le navigateur .

MDN propose 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;
  }
}
36
répondu André Laszlo 2016-03-02 12:50:59

de la Chaîne.prototype.répéter est maintenant la norme ES6.

'abc'.repeat(3); //abcabcabc
18
répondu Lewis 2015-04-07 04:33:16

expansion solution de P. Bailey :

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

de cette façon, vous devriez être à l'abri des types d'arguments inattendus:

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édits à jerone pour son élégance ++num idée!

17
répondu U-D13 2017-05-23 12:34:59

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

8
répondu Kalpesh Patel 2015-08-21 06:33:41
/**  
@desc: repeat string  
@param: n - times  
@param: d - delimiter  
*/

String.prototype.repeat = function (n, d) {
    return --n ? this + (d || '') + this.repeat(n, d) : '' + this
};

c'est comment répéter la chaîne de caractères plusieurs fois en utilisant le delimètre.

5
répondu BitOfUniverse 2013-05-09 02:24:17

voici une amélioration de 5-7% sur la réponse de disfated.

déroule la boucle en s'arrêtant à count > 1 et exécute une concat supplémentaire result += pattnern après la boucle. Cela évitera les boucles finales pattern += pattern inutilisées précédemment sans avoir à utiliser un si-check coûteux. Le résultat final ressemblerait à ceci:

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

et voici disfated's fiddle forked pour la version non roulée: http://jsfiddle.net/wsdfg/

4
répondu Dennis 2012-12-25 00:46:15
function repeat(s, n) { var r=""; for (var a=0;a<n;a++) r+=s; return r;}
2
répondu Joel Coehoorn 2008-10-14 20:17:15

essais des différentes méthodes:

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; i<n; i++)
            ret.push(s);
        return ret.join('');
    },
    concat: function (n,s) {
        if (n==0) return '';
        if (n==1 || isNaN(n)) return s;
        var ret = '';
        for (var i=0; i<n; i++)
            ret+=s;
        return ret;
    },
    artistoex: function (n,s) {
        var result = '';
        while (n>0) {
            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));
    }
}
2
répondu Fordi 2011-10-04 17:48:51

Voici la version sûre de JSLint

String.prototype.repeat = function (num) {
  var a = [];
  a.length = num << 0 + 1;
  return a.join(this);
};
2
répondu Erik Aigner 2013-05-09 02:15:45

pour tous les navigateurs

c'est aussi concis qu'il est:

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

si vous vous souciez aussi de la performance, c'est une bien meilleure approche:

function repeat(s, n) { var a=[],i=0;for(;i<n;)a[i++]=s;return a.join(''); }

si vous voulez comparer les performances des deux options, consultez ce violon et ce violon pour les tests de référence. Lors de mes propres tests, le la deuxième option était environ 2 fois plus rapide dans Firefox et environ 4 fois plus rapide dans Chrome!

pour les navigateurs modernes uniquement:

dans les navigateurs modernes, vous pouvez maintenant aussi faire ceci:

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

cette option est non seulement plus courte que les deux autres options, mais il est" 1519240920 encore plus rapide que la deuxième option.

malheureusement, il ne fonctionne dans aucune version de Internet explorer. Les nombres dans le tableau spécifient la première version de navigateur qui supporte pleinement la méthode:

enter image description here

2
répondu John Slegers 2016-02-25 18:06:26
function repeat(pattern, count) {
  for (var result = '';;) {
    if (count & 1) {
      result += pattern;
    }
    if (count >>= 1) {
      pattern += pattern;
    } else {
      return result;
    }
  }
}

vous pouvez le tester à JSFiddle . Comparé au hacky Array.join et le mien est, grosso modo, 10 (Chrome) à 100 (Safari) à 200 (Firefox) fois plus rapide (selon le navigateur).

2
répondu nikolay 2016-03-24 04:24:13

juste une autre fonction de répétition:

function repeat(s, n) {
  var str = '';
  for (var i = 0; i < n; i++) {
    str += s;
  }
  return str;
}
2
répondu oboshto 2017-01-28 00:36:23

ES2015 a été réalisé cette méthode repeat() !

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
2
répondu xgqfrms 2017-06-30 14:38:21

peut être le plus petit récursif: -

String.prototype.repeat = function(n,s) {
s = s || ""
if(n>0) {
   s += this
   s = this.repeat(--n,s)
}
return s}
1
répondu John 2011-07-02 14:54:55

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

function repeat(s, n){
    return ((new Array(n+1)).join(s));
}
alert(repeat('R', 10));
1
répondu Robin Rizvi 2014-08-05 09:57:10

simple concursive concatenation

je voulais juste lui donner une fête, et j'ai fait ceci:

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

ditto( "foo", "", 128 );

Je ne peux pas dire que je lui ai donné beaucoup de réflexion, et il montre probablement : -)

ce est sans doute meilleur

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

"foo".ditto( 128 );

et c'est un peu comme une réponse déjà postée - je le sais.

Mais pourquoi être récursive?

et comment A propos d'un petit comportement par défaut aussi?

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

"foo".ditto();

parce que , bien que la méthode non récursive va gérer arbitrairement de grandes répétitions sans frapper les limites de la pile d'appels, c'est beaucoup plus lent.

Pourquoi ai-je pris la peine d'ajouter d'autres méthodes qui ne sont pas à moitié aussi astucieuses que celles déjà publiées?

en partie pour mon propre plaisir, et en partie pour souligner de la manière la plus simple que je sais qu'il y a plusieurs façons de dépouiller un chat, et selon la situation, il est tout à fait possible que la méthode apparemment best n'est pas idéale.

une méthode relativement rapide et sophistiquée peut effectivement planter et brûler dans certaines circonstances, tandis qu'une méthode plus lente et plus simple peut faire le travail fait - éventuellement.

certaines méthodes peuvent être un peu plus que des exploits, et en tant que telles sujettes à être fixe hors de l'existence, et d'autres méthodes peuvent fonctionner magnifiquement dans toutes les conditions, mais sont construits de sorte que un tout simplement n'a aucune idée de la façon dont il fonctionne.

" et si je ne sais pas comment ça marche?!"

sérieusement?

JavaScript souffre d'une de ses plus grandes forces; il est très tolérant du mauvais comportement, et si flexible qu'il va se pencher en arrière pour rendre des résultats, quand il pourrait avoir été mieux pour tout le monde si il avait cassé!

"avec un grand pouvoir, vient une grande responsabilité" ; -)

mais plus sérieusement et surtout, bien que des questions générales comme celle-ci conduisent à l'étonnement sous la forme de intelligent réponses que si rien d'autre, élargir ses connaissances et des horizons, à la fin, la tâche à portée de main - le scénario pratique qui utilise la méthode résultant - peut exiger un peu moins, ou un peu plus intelligent que ce qui est suggéré.

ces " perfect " les algorithmes sont amusants et tout, mais "one size fits all" sera rarement, voire jamais mieux que sur mesure.

ce sermon vous a été apporté avec courtoisie d'un manque de sommeil et d'un intérêt passager. Aller de l'avant et de code!

1
répondu Fred Gandt 2014-10-19 22:00:09

tout d'abord, les questions de L'OP semblent être sur la concision - ce que je comprends à dire "simple et facile à lire", alors que la plupart des réponses semblent être sur l'efficacité - ce qui n'est évidemment pas la même chose et aussi je pense que si vous mettez en œuvre certains algorithmes très spécifiques de manipulation de grandes données, ne devrait pas vous inquiéter lorsque vous venez à mettre en œuvre des fonctions de base Javascript manipulation de données. La concision est beaucoup plus importante.

Deuxièmement, comme André Laszlo a noté, à la Chaîne.repeat fait partie D'ECMAScript 6 et est déjà disponible dans plusieurs implémentations populaires - donc l'implémentation la plus concise de String.repeat n'est pas de l'implémenter; -)

enfin, si vous avez besoin de prendre en charge des hôtes qui n'offrent pas L'implémentation D'ECMAScript 6, le polyfill de MDN mentionné par André Laszlo est tout sauf concis.

donc, sans plus attendre-voici mon concis polyfill:

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

Oui, c'est une récursivité. J'aime récurrences - ils sont simples et si c'est bien fait et facile à comprendre. En ce qui concerne l'efficacité, si le langage le supporte, ils peuvent être très efficaces s'ils sont écrits correctement.

D'après mes tests, cette méthode est environ 60% plus rapide que l'approche Array.join . Bien qu'elle ne vienne évidemment nulle part la mise en œuvre de disfated proche, il est beaucoup plus simple que les deux.

ma configuration de test est node v0.10, en utilisant "Mode Strict "(je pense qu'il permet une sorte de TCO ), appelant repeat(1000) sur une chaîne de 10 caractères un million de fois.

1
répondu Guss 2014-12-18 14:23:10

utilisez Lodash pour les fonctionnalités utilitaires Javascript, comme répéter des chaînes.

Lodash offre de belles performances et une compatibilité ECMAScript.

je le recommande fortement pour le développement de L'interface utilisateur et il fonctionne bien Côté Serveur, aussi.

Voici comment répéter la chaîne "yo" 2 fois en utilisant Lodash:

> _.repeat('yo', 2)
"yoyo"
1
répondu l3x 2015-11-04 19:55:11

solution Récursive à l'aide de diviser pour régner:

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); }
}
0
répondu Fordi 2011-10-04 14:28:05

je suis venu ici au hasard et n'ai jamais eu une raison de répéter un char en javascript avant.

j'ai été impressionné par la façon de faire d'artistoex et les résultats de disfated. J'ai remarqué que la dernière chaîne concat n'était pas nécessaire, comme Dennis l'a également souligné.

j'ai remarqué un peu plus de choses en jouant avec l'échantillonnage disfated mettre ensemble.

les résultats ont varié un bon nombre souvent en faveur de la dernière exécution et des algorithmes similaires souvent jockey pour la position. Une des choses que j'ai changées était au lieu d'utiliser le compte généré par JSLitmus comme graine pour les appels; comme le compte a été généré différent pour les différentes méthodes, j'ai mis un index. Cela rendait la chose beaucoup plus fiable. J'ai ensuite veillé à ce que des cordes de différentes tailles soient passées aux fonctions. Cela a empêché certaines des variations que j'ai vu, où certains algorithmes ont fait mieux à la charrue simple ou des cordes plus petites. Cependant, le top 3 des méthodes tout a fait bien quelle que soit la taille de la chaîne.

fourked 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('<h1>Error in "' + name + '"</h1>');
});

JSLitmus.runAll();

J'ai alors Inclus Le fix de Dennis et j'ai décidé de voir si je pouvais trouver un moyen de sortir un peu plus.

comme javascript ne peut pas vraiment optimiser les choses, la meilleure façon d'améliorer les performances est d'éviter les choses manuellement. Si j'ai pris les 4 premiers résultats triviaux hors de la boucle, je pourrait éviter 2-4 magasins de chaîne de caractères et écrire le magasin final directement au résultat.

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

cela s'est traduit par une amélioration de 1 à 2% en moyenne par rapport au correctif de Dennis. Cependant, des passages différents et des navigateurs différents montreraient une variance assez juste que ce code supplémentaire ne vaut probablement pas l'effort par rapport aux 2 algorithmes précédents.

Un tableau

Edit: je l'ai fait principalement sous chrome. Firefox et IE will favorise souvent Dennis de quelques pour cent.

0
répondu Andrew Hallendorff 2013-03-15 04:21:30

méthode Simple:

String.prototype.repeat = function(num) {
    num = parseInt(num);
    if (num < 0) return '';
    return new Array(num + 1).join(this);
}
0
répondu Eduardo Cuomo 2013-05-28 20:33:25

les Gens compliquer ce ridicule de mesure ou de déchets de performance. Les tableaux? La récursivité? Vous vous moquez de moi.

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

Edit. j'ai fait quelques tests simples pour comparer avec la version bitwise Postée par artistoex / disfated et un tas d'autres personnes. Ce dernier n'était que légèrement plus rapide, mais des ordres de grandeur plus économes en mémoire. Pour 1000000 répétitions du mot 'blah', le processus de noeud est monté à 46 mégaoctets avec l'algorithme de concaténation simple (ci-dessus), mais seulement 5,5 mégaoctets avec l'algorithme logarithmique. Ce dernier point est certainement la voie à suivre.

function repeat (string, times) {
  var result = ''
  while (times > 0) {
    if (times & 1) result += string
    times >>= 1
    string += string
  }
  return result
}
0
répondu Nelo Mitranim 2014-08-31 12:19:08

si vous pensez que toutes les définitions de prototypes, les créations de tableaux et les opérations de jointure sont exagérées, utilisez simplement un code en ligne là où vous en avez besoin. String s répétant N fois:

for (var i = 0, result = ''; i < N; i++) result += S;
0
répondu Semra 2015-01-25 12:13:58

chaînes Concaténantes basées sur un nombre.

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

Espère que ça aide!

0
répondu Bishop 2016-04-17 15:48:28