JavaScript'te n boyutlu parçalar halinde büyük dize bölme


208

Çok büyük bir dizeyi (diyelim ki 10.000 karakter) N boyutlu parçalara bölmek istiyorum.

Bunu yapmanın performans açısından en iyi yolu ne olurdu?

Örneğin: "1234567890"2'ye bölünür ["12", "34", "56", "78", "90"].

Böyle bir şey kullanmak mümkün olabilir mi String.prototype.matchve eğer öyleyse, bunu performans açısından yapmanın en iyi yolu olurdu?

Yanıtlar:


456

Bunun gibi bir şey yapabilirsiniz:

"1234567890".match(/.{1,2}/g);
// Results in:
["12", "34", "56", "78", "90"]

Yöntem, boyutu yığın boyutunun tam bir katı olmayan dizelerle çalışmaya devam edecektir:

"123456789".match(/.{1,2}/g);
// Results in:
["12", "34", "56", "78", "9"]

Genel olarak, en fazla n boyutlu alt dizeden çıkarmak istediğiniz herhangi bir dize için şunları yaparsınız:

str.match(/.{1,n}/g); // Replace n with the size of the substring

Dizeniz satırsonu veya satır başı içeriyorsa şunları yaparsınız:

str.match(/(.|[\r\n]){1,n}/g); // Replace n with the size of the substring

Performansa gelince, bunu yaklaşık 10 bin karakterle denedim ve Chrome'da bir saniyeden biraz fazla sürdü. YMMV.

Bu, yeniden kullanılabilir bir fonksiyonda da kullanılabilir:

function chunkString(str, length) {
  return str.match(new RegExp('.{1,' + length + '}', 'g'));
}

8
Bu cevap şimdi yaklaşık 3 yaşında olduğu için, @Vivin tarafından yapılan performans testini tekrar denemek istedim. Dolayısıyla, FYI, verilen normal ifadeyi kullanarak 100 bin karakteri ikiye bölmek Chrome v33'te anlıktır.
aymericbeaumet

1
@Fmstrat "Dizeniz boşluk içeriyorsa, uzunluk olarak sayılmaz" ile ne demek istersiniz? Evet, .yeni satırla hiç eşleşmiyor. Cevabı alacak \nve \rdikkate alacak şekilde güncelleyeceğim .
Vivin Paliath

2
Gibi bir şey var chunks = str.split("").reverse().join().match(/.{1, 4}/).map(function(s) { return s.split("").reverse().join(); });. Bu, 4'lü parçalar halinde yapar. "Daha az veya daha fazla" ile ne demek istediğinizden emin değilim. Bunun, özellikle karakterleri birleştiren ve Unicode dizelerini de kırabileceğiniz dizelerde genel olarak işe yaramayacağını unutmayın.
Vivin Paliath

2
Developer.mozilla.org/tr-TR/docs/Web/JavaScript/Reference/… 'a göre , yeni satırlar da dahil olmak üzere herhangi bir karakteri ile eşleştirebilirsiniz [^]. Bu, örneğin sonuçstr.match(/[^]{1,n}/g)
Francesc Rosas

1
JSperf'de performans ölçütleriyle gerçekten hızlı dize parçalaması arayan herkes için cevabımı görün . Normal ifade kullanmak, en yavaş yığınlama yöntemidir.
Justin Warkentin

34

JsPerf'de görebileceğiniz birkaç daha hızlı varyant oluşturdum . En sevdiğim şu:

function chunkSubstr(str, size) {
  const numChunks = Math.ceil(str.length / size)
  const chunks = new Array(numChunks)

  for (let i = 0, o = 0; i < numChunks; ++i, o += size) {
    chunks[i] = str.substr(o, size)
  }

  return chunks
}

2
bu yüzden son dizinin döndürülmediği için boyutu 20'ye ayarladığım durumlar dışında uzun dizelerde (yaklaşık 800k - 9m karakter) harika çalıştı ... çok garip davranış.
David

1
@DavidAnderton İyi yakaladın. Bunu düzelttim ve ilginç bir şekilde daha hızlı çalışıyor gibi görünüyor. Math.ceil()Doğru sayıda parçayı belirlemek için ne zaman yapılması gerektiği yuvarlanıyordu .
Justin Warkentin

1
Teşekkürler! İsteğe bağlı Unicode desteğiyle bir NPM modülü olarak bir araya getirdim
Vlad Holubiev

33

Sonuç olarak:

  • matchçok verimsiz, slicedaha iyi, Firefox'ta substr/ substringdaha iyi
  • match kısa dizeler için daha da verimsizdir (önbelleğe alınmış normal ifade ile bile - muhtemelen normal ifade ayrıştırma kurulum süresi nedeniyle)
  • match büyük yığın boyutu için daha da verimsizdir (muhtemelen "zıplayamama" nedeniyle)
  • çok küçük yığın boyutuna sahip daha uzun dizeler için eski IE'de matchdaha iyi performans gösterir slice, ancak diğer tüm sistemlerde yine de kaybeder
  • jsperf kayalar

19

Bu hızlı ve kolay bir çözümdür -

function chunkString (str, len) {
  const size = Math.ceil(str.length/len)
  const r = Array(size)
  let offset = 0
  
  for (let i = 0; i < size; i++) {
    r[i] = str.substr(offset, len)
    offset += len
  }
  
  return r
}

console.log(chunkString("helloworld", 3))
// => [ "hel", "low", "orl", "d" ]

// 10,000 char string
const bigString = "helloworld".repeat(1000)
console.time("perf")
const result = chunkString(bigString, 3)
console.timeEnd("perf")
console.log(result)
// => perf: 0.385 ms
// => [ "hel", "low", "orl", "dhe", "llo", "wor", ... ]


1
Bunun substr()yerine kullanmak zorundasınız substring().
Leif

2
Merak ediyorum, neden değişken isimlerindeki alt çizgiler?
Felipe Valdes

@FelipeValdes Bunları global / parametre değişkenleriyle karıştırmamayı veya özel olarak değerlendirildiğini varsayıyorum.
Bay Polywhirl

15

Sürpriz! Bölmek için bölmeyi kullanabilirsiniz .

var parts = "1234567890 ".split(/(.{2})/).filter(O=>O)

Sonuçları [ '12', '34', '56', '78', '90', ' ' ]


Bu en harika cevap.
galkin

2
Ne filter (o=>o)için?
Ben Carp

2
current regex, parçalar arasında boş dizi öğeleri oluşturur. filter(x=>x)bu boş öğeleri filtrelemek için kullanılır
artemdev

Kısa ve akıllıdır ancak girişin üzerinde defalarca yinelenir. Bu yanıt, bu konudaki diğer çözümlerden 4 kat daha yavaştır.
Teşekkürler

6
@BenCarp Motosiklet operatörü. Daha hızlı gitmesini sağlar. ;)
Fozi

7
var str = "123456789";
var chunks = [];
var chunkSize = 2;

while (str) {
    if (str.length < chunkSize) {
        chunks.push(str);
        break;
    }
    else {
        chunks.push(str.substr(0, chunkSize));
        str = str.substr(chunkSize);
    }
}

alert(chunks); // chunks == 12,34,56,78,9

5

Genişletilmiş bir işlev yazdım, bu yüzden yığın uzunluğu da bir dizi sayı olabilir, [1,3]

String.prototype.chunkString = function(len) {
    var _ret;
    if (this.length < 1) {
        return [];
    }
    if (typeof len === 'number' && len > 0) {
        var _size = Math.ceil(this.length / len), _offset = 0;
        _ret = new Array(_size);
        for (var _i = 0; _i < _size; _i++) {
            _ret[_i] = this.substring(_offset, _offset = _offset + len);
        }
    }
    else if (typeof len === 'object' && len.length) {
        var n = 0, l = this.length, chunk, that = this;
        _ret = [];
        do {
            len.forEach(function(o) {
                chunk = that.substring(n, n + o);
                if (chunk !== '') {
                    _ret.push(chunk);
                    n += chunk.length;
                }
            });
            if (n === 0) {
                return undefined; // prevent an endless loop when len = [0]
            }
        } while (n < l);
    }
    return _ret;
};

Kod

"1234567890123".chunkString([1,3])

dönecek:

[ '1', '234', '5', '678', '9', '012', '3' ]

4

Split'in büyük dizesini verilen kelimelerin Küçük dizelerine alır .

function chunkSubstr(str, words) {
  var parts = str.split(" ") , values = [] , i = 0 , tmpVar = "";
  $.each(parts, function(index, value) {
      if(tmpVar.length < words){
          tmpVar += " " + value;
      }else{
          values[i] = tmpVar.replace(/\s+/g, " ");
          i++;
          tmpVar = value;
      }
  });
  if(values.length < 1 &&  parts.length > 0){
      values[0] = tmpVar;
  }
  return values;
}

3
var l = str.length, lc = 0, chunks = [], c = 0, chunkSize = 2;
for (; lc < l; c++) {
  chunks[c] = str.slice(lc, lc += chunkSize);
}

2

Normal ifade kullanırdım ...

var chunkStr = function(str, chunkLength) {
    return str.match(new RegExp('[\\s\\S]{1,' + +chunkLength + '}', 'g'));
}

1
const getChunksFromString = (str, chunkSize) => {
    var regexChunk = new RegExp(`.{1,${chunkSize}}`, 'g')   // '.' represents any character
    return str.match(regexChunk)
}

Gerektiğinde ara

console.log(getChunksFromString("Hello world", 3))   // ["Hel", "lo ", "wor", "ld"]

1

İşte biraz denemeden sonra şablon dizeleri için bulduğum bir çözüm:

Kullanımı:

chunkString(5)`testing123`

function chunkString(nSize) {
    return (strToChunk) => {
        let result = [];
        let chars = String(strToChunk).split('');

        for(let i = 0; i < (String(strToChunk).length / nSize); i++) {
            result = result.concat(chars.slice(i*nSize,(i+1)*nSize).join(''));
        }
        return result
    }
}

document.write(chunkString(5)`testing123`);
// returns: testi,ng123

document.write(chunkString(3)`testing123`);
// returns: tes,tin,g12,3


1

reduce()Herhangi bir normal ifade olmadan kullanabilirsiniz :

(str, n) => {
  return str.split('').reduce(
    (acc, rec, index) => {
      return ((index % n) || !(index)) ? acc.concat(rec) : acc.concat(',', rec)
    },
    ''
  ).split(',')
}

reduceYönteminizi nasıl kullanacağınıza ilişkin örnekler verirseniz çok yardımcı olacağını düşünüyorum .
kiatng

0

Bir prototip işlevi şeklinde:

String.prototype.lsplit = function(){
    return this.match(new RegExp('.{1,'+ ((arguments.length==1)?(isFinite(String(arguments[0]).trim())?arguments[0]:false):1) +'}', 'g'));
}

0

İşte kullanıyorum kodu, String.prototype.slice kullanır .

Evet, mevcut standartları olabildiğince yakın takip etmeye çalıştığından ve elbette makul miktarda JSDOC yorumu içerdiğinden, bir yanıt gittikçe oldukça uzundur . Ancak, küçültüldüğünde, kod yalnızca 828 bayttır ve iletim için sıkıştırıldıktan sonra sadece 497 bayttır.

Bunun eklediği 1 yöntemi String.prototype(varsa Object.defineProperty kullanarak ):

  1. toChunks

İşlevselliği kontrol etmek için bir dizi test eklenmiştir.

Kod uzunluğunun performansı etkileyeceğinden mi endişeleniyorsunuz? Endişelenmenize gerek yok, http://jsperf.com/chunk-string/3

Ekstra kodun çoğu, kodun birden fazla javascript ortamında aynı şekilde yanıt vereceğinden emin olmak için vardır.

/*jslint maxlen:80, browser:true, devel:true */

/*
 * Properties used by toChunks.
 */

/*property
    MAX_SAFE_INTEGER, abs, ceil, configurable, defineProperty, enumerable,
    floor, length, max, min, pow, prototype, slice, toChunks, value,
    writable
*/

/*
 * Properties used in the testing of toChunks implimentation.
 */

/*property
    appendChild, createTextNode, floor, fromCharCode, getElementById, length,
    log, pow, push, random, toChunks
*/

(function () {
    'use strict';

    var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || Math.pow(2, 53) - 1;

    /**
     * Defines a new property directly on an object, or modifies an existing
     * property on an object, and returns the object.
     *
     * @private
     * @function
     * @param {Object} object
     * @param {string} property
     * @param {Object} descriptor
     * @return {Object}
     * @see https://goo.gl/CZnEqg
     */
    function $defineProperty(object, property, descriptor) {
        if (Object.defineProperty) {
            Object.defineProperty(object, property, descriptor);
        } else {
            object[property] = descriptor.value;
        }

        return object;
    }

    /**
     * Returns true if the operands are strictly equal with no type conversion.
     *
     * @private
     * @function
     * @param {*} a
     * @param {*} b
     * @return {boolean}
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.4
     */
    function $strictEqual(a, b) {
        return a === b;
    }

    /**
     * Returns true if the operand inputArg is undefined.
     *
     * @private
     * @function
     * @param {*} inputArg
     * @return {boolean}
     */
    function $isUndefined(inputArg) {
        return $strictEqual(typeof inputArg, 'undefined');
    }

    /**
     * The abstract operation throws an error if its argument is a value that
     * cannot be converted to an Object, otherwise returns the argument.
     *
     * @private
     * @function
     * @param {*} inputArg The object to be tested.
     * @throws {TypeError} If inputArg is null or undefined.
     * @return {*} The inputArg if coercible.
     * @see https://goo.gl/5GcmVq
     */
    function $requireObjectCoercible(inputArg) {
        var errStr;

        if (inputArg === null || $isUndefined(inputArg)) {
            errStr = 'Cannot convert argument to object: ' + inputArg;
            throw new TypeError(errStr);
        }

        return inputArg;
    }

    /**
     * The abstract operation converts its argument to a value of type string
     *
     * @private
     * @function
     * @param {*} inputArg
     * @return {string}
     * @see https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tostring
     */
    function $toString(inputArg) {
        var type,
            val;

        if (inputArg === null) {
            val = 'null';
        } else {
            type = typeof inputArg;
            if (type === 'string') {
                val = inputArg;
            } else if (type === 'undefined') {
                val = type;
            } else {
                if (type === 'symbol') {
                    throw new TypeError('Cannot convert symbol to string');
                }

                val = String(inputArg);
            }
        }

        return val;
    }

    /**
     * Returns a string only if the arguments is coercible otherwise throws an
     * error.
     *
     * @private
     * @function
     * @param {*} inputArg
     * @throws {TypeError} If inputArg is null or undefined.
     * @return {string}
     */
    function $onlyCoercibleToString(inputArg) {
        return $toString($requireObjectCoercible(inputArg));
    }

    /**
     * The function evaluates the passed value and converts it to an integer.
     *
     * @private
     * @function
     * @param {*} inputArg The object to be converted to an integer.
     * @return {number} If the target value is NaN, null or undefined, 0 is
     *                   returned. If the target value is false, 0 is returned
     *                   and if true, 1 is returned.
     * @see http://www.ecma-international.org/ecma-262/5.1/#sec-9.4
     */
    function $toInteger(inputArg) {
        var number = +inputArg,
            val = 0;

        if ($strictEqual(number, number)) {
            if (!number || number === Infinity || number === -Infinity) {
                val = number;
            } else {
                val = (number > 0 || -1) * Math.floor(Math.abs(number));
            }
        }

        return val;
    }

    /**
     * The abstract operation ToLength converts its argument to an integer
     * suitable for use as the length of an array-like object.
     *
     * @private
     * @function
     * @param {*} inputArg The object to be converted to a length.
     * @return {number} If len <= +0 then +0 else if len is +INFINITY then
     *                   2^53-1 else min(len, 2^53-1).
     * @see https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
     */
    function $toLength(inputArg) {
        return Math.min(Math.max($toInteger(inputArg), 0), MAX_SAFE_INTEGER);
    }

    if (!String.prototype.toChunks) {
        /**
         * This method chunks a string into an array of strings of a specified
         * chunk size.
         *
         * @function
         * @this {string} The string to be chunked.
         * @param {Number} chunkSize The size of the chunks that the string will
         *                           be chunked into.
         * @returns {Array} Returns an array of the chunked string.
         */
        $defineProperty(String.prototype, 'toChunks', {
            enumerable: false,
            configurable: true,
            writable: true,
            value: function (chunkSize) {
                var str = $onlyCoercibleToString(this),
                    chunkLength = $toInteger(chunkSize),
                    chunked = [],
                    numChunks,
                    length,
                    index,
                    start,
                    end;

                if (chunkLength < 1) {
                    return chunked;
                }

                length = $toLength(str.length);
                numChunks = Math.ceil(length / chunkLength);
                index = 0;
                start = 0;
                end = chunkLength;
                chunked.length = numChunks;
                while (index < numChunks) {
                    chunked[index] = str.slice(start, end);
                    start = end;
                    end += chunkLength;
                    index += 1;
                }

                return chunked;
            }
        });
    }
}());

/*
 * Some tests
 */

(function () {
    'use strict';

    var pre = document.getElementById('out'),
        chunkSizes = [],
        maxChunkSize = 512,
        testString = '',
        maxTestString = 100000,
        chunkSize = 0,
        index = 1;

    while (chunkSize < maxChunkSize) {
        chunkSize = Math.pow(2, index);
        chunkSizes.push(chunkSize);
        index += 1;
    }

    index = 0;
    while (index < maxTestString) {
        testString += String.fromCharCode(Math.floor(Math.random() * 95) + 32);
        index += 1;
    }

    function log(result) {
        pre.appendChild(document.createTextNode(result + '\n'));
    }

    function test() {
        var strLength = testString.length,
            czLength = chunkSizes.length,
            czIndex = 0,
            czValue,
            result,
            numChunks,
            pass;

        while (czIndex < czLength) {
            czValue = chunkSizes[czIndex];
            numChunks = Math.ceil(strLength / czValue);
            result = testString.toChunks(czValue);
            czIndex += 1;
            log('chunksize: ' + czValue);
            log(' Number of chunks:');
            log('  Calculated: ' + numChunks);
            log('  Actual:' + result.length);
            pass = result.length === numChunks;
            log(' First chunk size: ' + result[0].length);
            pass = pass && result[0].length === czValue;
            log(' Passed: ' + pass);
            log('');
        }
    }

    test();
    log('');
    log('Simple test result');
    log('abcdefghijklmnopqrstuvwxyz'.toChunks(3));
}());
<pre id="out"></pre>


0

Slice () yöntemini kullanarak:

function returnChunksArray(str, chunkSize) {
  var arr = [];
  while(str !== '') {
    arr.push(str.slice(0, chunkSize));
    str = str.slice(chunkSize);
  }
  return arr;
}

Aynı şey substring () yöntemi kullanılarak da yapılabilir.

function returnChunksArray(str, chunkSize) {
  var arr = [];
  while(str !== '') {
    arr.push(str.substring(0, chunkSize));
    str = str.substring(chunkSize);
  }
  return arr;
}

0

Yukarıdaki çözümle ilgili sorunum, cümledeki konumdan bağımsız olarak ipi resmi boyut yığınlarına taşımasıdır.

Aşağıdaki daha iyi bir yaklaşım düşünüyorum; bazı performans ayarlarına ihtiyaç duymasına rağmen:

 static chunkString(str, length, size,delimiter='\n' ) {
        const result = [];
        for (let i = 0; i < str.length; i++) {
            const lastIndex = _.lastIndexOf(str, delimiter,size + i);
            result.push(str.substr(i, lastIndex - i));
            i = lastIndex;
        }
        return result;
    }

0

Kesinlikle böyle bir şey yapabilirsiniz

let pieces = "1234567890 ".split(/(.{2})/).filter(x => x.length == 2);

bunu elde etmek için:

[ '12', '34', '56', '78', '90' ]

Parça boyutunu n boyutunda olacak şekilde dinamik olarak girmek / ayarlamak istiyorsanız, bunu yapabilirsiniz:

n = 2;
let pieces = "1234567890 ".split(new RegExp("(.{"+n.toString()+"})")).filter(x => x.length == n);

Orijinal dizede olası tüm boyut n yığınlarını bulmak için şunu deneyin:

let subs = new Set();
let n = 2;
let str = "1234567890 ";
let regex = new RegExp("(.{"+n.toString()+"})");     //set up regex expression dynamically encoded with n

for (let i = 0; i < n; i++){               //starting from all possible offsets from position 0 in the string
    let pieces = str.split(regex).filter(x => x.length == n);    //divide the string into chunks of size n...
    for (let p of pieces)                 //...and add the chunks to the set
        subs.add(p);
    str = str.substr(1);    //shift the string reading frame
}

Şununla bitirmelisiniz:

[ '12', '23', '34', '45', '56', '67', '78', '89', '90', '0 ' ]

-1
    window.format = function(b, a) {
        if (!b || isNaN(+a)) return a;
        var a = b.charAt(0) == "-" ? -a : +a,
            j = a < 0 ? a = -a : 0,
            e = b.match(/[^\d\-\+#]/g),
            h = e && e[e.length - 1] || ".",
            e = e && e[1] && e[0] || ",",
            b = b.split(h),
            a = a.toFixed(b[1] && b[1].length),
            a = +a + "",
            d = b[1] && b[1].lastIndexOf("0"),
            c = a.split(".");
        if (!c[1] || c[1] && c[1].length <= d) a = (+a).toFixed(d + 1);
        d = b[0].split(e);
        b[0] = d.join("");
        var f = b[0] && b[0].indexOf("0");
        if (f > -1)
            for (; c[0].length < b[0].length - f;) c[0] = "0" + c[0];
        else +c[0] == 0 && (c[0] = "");
        a = a.split(".");
        a[0] = c[0];
        if (c = d[1] && d[d.length -
                1].length) {
            for (var d = a[0], f = "", k = d.length % c, g = 0, i = d.length; g < i; g++) f += d.charAt(g), !((g - k + 1) % c) && g < i - c && (f += e);
            a[0] = f
        }
        a[1] = b[1] && a[1] ? h + a[1] : "";
        return (j ? "-" : "") + a[0] + a[1]
    };

var str="1234567890";
var formatstr=format( "##,###.", str);
alert(formatstr);


This will split the string in reverse order with comma separated after 3 char's. If you want you can change the position.

-1

Bu küçük kod parçasına ne dersiniz?

function splitME(str, size) {
    let subStr = new RegExp('.{1,' + size + '}', 'g');
    return str.match(subStr);
};

-2
function chunkString(str, length = 10) {
    let result = [],
        offset = 0;
    if (str.length <= length) return result.push(str) && result;
    while (offset < str.length) {
        result.push(str.substr(offset, length));
        offset += length;
    }
    return result;
}

4
Cevabınız yeni bir şey eklemiyor (diğer cevaplara kıyasla) ve diğer cevaplar gibi açıklama içermiyor.
flob
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.