[javascript] 채워지지 않은 JavaScript 배열을 만드는 가장 효율적인 방법은 무엇입니까?

JavaScript에서 임의의 길이 0으로 채워진 배열을 만드는 가장 효율적인 방법은 무엇입니까?



답변

ES6가 소개 Array.prototype.fill합니다. 다음과 같이 사용할 수 있습니다 :

new Array(len).fill(0);

그것이 빠르면 확실하지 않지만 짧고 자체 설명하기 때문에 좋아합니다.

여전히 IE 에는 없지만 ( 호환성 검사 ) polyfill을 사용할 수 있습니다 .


답변

이것은 오래된 실이지만 2 센트를 더하고 싶었습니다. 이것이 얼마나 느리거나 빠른지 잘 모르겠지만 빠른 라이너입니다. 여기 내가하는 일이 있습니다.

숫자로 미리 채우려면 :

Array.apply(null, Array(5)).map(Number.prototype.valueOf,0);
// [0, 0, 0, 0, 0]

문자열로 미리 채우려면 :

Array.apply(null, Array(3)).map(String.prototype.valueOf,"hi")
// ["hi", "hi", "hi"]

다른 답변은 다음과 같이 제안했습니다.

new Array(5+1).join('0').split('')
// ["0", "0", "0", "0", "0"]

그러나 “0”이 아닌 0 (숫자) (문자열 내부의 0)을 원하면 다음을 수행 할 수 있습니다.

new Array(5+1).join('0').split('').map(parseFloat)
// [0, 0, 0, 0, 0]

답변

사전 계산 된 값으로 배열을 채우는 우아한 방법

지금까지 아무도 언급하지 않은 ES6을 사용하여 수행하는 또 다른 방법은 다음과 같습니다.

> Array.from(Array(3), () => 0)
< [0, 0, 0]

지도 함수를의 두 번째 매개 변수로 전달하여 작동합니다 Array.from.

위의 예에서 첫 번째 매개 변수는 값으로 채워진 3 개의 위치로 구성된 배열을 할당 한 undefined다음 람다 함수가 이들 각각을 값에 매핑합니다 0.

Array(len).fill(0)더 짧지 만 계산을 먼저 수행하여 배열을 채워야하는 경우 작동하지 않습니다 (질문은 요청하지 않았지만 많은 사람들이 여기를 찾습니다) .

예를 들어 10 개의 난수를 가진 배열이 필요한 경우 :

> Array.from(Array(10), () => Math.floor(10 * Math.random()))
< [3, 6, 8, 1, 9, 3, 0, 6, 7, 1]

동등한 것보다 더 간결하고 우아합니다.

const numbers = Array(10);
for (let i = 0; i < numbers.length; i++) {
    numbers[i] = Math.round(10 * Math.random());
}

이 메소드는 콜백에 제공된 인덱스 매개 변수를 활용하여 일련의 숫자를 생성하는 데에도 사용할 수 있습니다.

> Array.from(Array(10), (d, i) => i)
< [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

보너스 답변 : 문자열을 사용하여 배열을 채우십시오 repeat()

이 답변은 많은 관심을 받고 있기 때문에이 멋진 트릭을 보여주고 싶었습니다. 내 주요 대답만큼 유용하지는 않지만 여전히 알려지지 않았지만 매우 유용한 String repeat()메서드를 소개합니다. 요령은 다음과 같습니다.

> "?".repeat(10).split("").map(() => Math.floor(10 * Math.random()))
< [5, 6, 3, 5, 0, 8, 2, 7, 4, 1]

시원 해요? repeat()원래 문자열을 특정 횟수만큼 반복하는 문자열을 만드는 데 유용한 방법입니다. 그 후 split()우리를 위해 배열을 만든 다음 map()원하는 값으로 ping합니다. 단계별로 분류 :

> "?".repeat(10)
< "??????????"

> "?".repeat(10).split("")
< ["?", "?", "?", "?", "?", "?", "?", "?", "?", "?"]

> "?".repeat(10).split("").map(() => Math.floor(10 * Math.random()))
< [5, 6, 3, 5, 0, 8, 2, 7, 4, 1]

답변

한마디로

가장 빠른 솔루션

let a = new Array(n); for (let i=0; i<n; ++i) a[i] = 0;

가장 짧은 (핸디) 솔루션

Array(n).fill(0)


세부

오늘 2020.06.09 Chrome 83.0, Firefox 77.0 및 Safari 13.1 브라우저에서 macOS High Sierra 10.13.6에서 테스트를 수행합니다. 두 가지 테스트 사례에 대해 선택한 솔루션을 테스트합니다.

  • 10 개의 요소가있는 작은 배열- 여기에서 테스트를 수행 할 수 있습니다
  • 1M 요소의 큰 배열- 여기에서 테스트를 수행 할 수 있습니다

결론

  • new Array(n)+for(N) 기반 솔루션은 소형 어레이 및 대형 어레이에 가장 빠른 솔루션이며 (Chrome 제외하지만 여전히 빠름) 빠른 크로스 브라우저 솔루션으로 권장됩니다.
  • new Float32Array(n)(I) 기반 솔루션 은 비 전형적인 배열을 반환 push(..)하므로 (예를 들어 호출 할 수 없음 ) 결과를 다른 솔루션과 비교할 수는 없지만이 솔루션은 모든 브라우저에서 큰 배열의 다른 솔루션보다 약 10-20 배 빠릅니다.
  • for(L, M, N, O) 기반 솔루션 은 소형 어레이에 빠릅니다.
  • fill(B, C) 기반 솔루션 은 Chrome 및 Safari에서 빠르지 만 큰 배열의 경우 Firefox에서 가장 느립니다. 그들은 작은 배열에 대해 중간 속도입니다
  • Array.apply(P) 기반 솔루션 은 큰 배열에서 오류를 발생시킵니다.

여기에 이미지 설명을 입력하십시오

코드와 예제

아래 코드는 측정에 사용되는 솔루션을 나타냅니다.

Chrome의 결과 예

여기에 이미지 설명을 입력하십시오


답변

이미 언급 한 ES 6 채우기 방법은이를 잘 처리합니다. 대부분의 최신 데스크탑 브라우저는 이미 현재 필요한 배열 프로토 타입 방법 (Chromium, FF, Edge 및 Safari)을 지원합니다 [ 1 ]. MDN에 대한 세부 정보를 찾을 수 있습니다 . 간단한 사용법 예는

a = new Array(10).fill(0);

현재 브라우저 지원이 제공되므로 사용자가 최신 데스크탑 브라우저를 사용하고 있는지 확신 할 수없는 경우이를 사용하는 것이 좋습니다.


답변

참고 : 2013 년 8 월 추가, 2015 년 2 월 업데이트 : 2009 년 아래 답변은 JavaScript의 일반 Array유형과 관련이 있습니다. ES2015에 정의되어 있고 현재 많은 브라우저에서 사용 가능한 최신 유형의 배열 과 관련이 없습니다 Int32Array. 또한 ES2015은 추가 점에 유의 fill모두 방법을 배열 하고 입력 배열 을 채우기 위해 가장 효율적인 방법이 될 것입니다 …

또한 배열을 만드는 방법에 따라 구현에 큰 차이가 생길 수 있습니다. 특히 Chrome의 V8 엔진은 가능한 경우 고효율의 연속 메모리 배열을 사용하여 필요할 때만 객체 기반 배열로 이동하려고 시도합니다.


대부분의 언어에서는 다음과 같이 사전 할당 된 후 0으로 채워집니다.

function newFilledArray(len, val) {
    var rv = new Array(len);
    while (--len >= 0) {
        rv[len] = val;
    }
    return rv;
}

그러나 JavaScript 배열 은 실제로 배열 이 아니며 다른 모든 JavaScript 객체와 마찬가지로 키 / 값 맵이므로 “할당”할 필요가 없습니다 (길이를 설정해도 많은 슬롯을 채울 수는 없습니다). 구현이 키 처리를 최적화했을 때 키를 역순으로 추가하여 0으로 카운트 다운하는 이점 (루프에서 비교를 빠르게하는 것)이 중요하지 않다고 믿을만한 이유가 있습니까? 이론적으로 배열과 관련하여 일반적으로 순서대로 수행합니다.

실제로 Matthew Crumley는 Firefox에서 카운트 다운이 카운트 업보다 카운트 다운이 현저히 느리다는 것을 지적했습니다. 결과는 확인할 수 있습니다. 배열의 일부입니다 (0에서 루핑하는 것이 var의 한계까지 루핑하는 것보다 여전히 빠릅니다). Firefox에서 요소를 역순으로 배열에 추가하는 것은 분명히 느린 작업입니다. 실제로 결과는 JavaScript 구현에 따라 상당히 다양합니다 (놀라운 것은 아닙니다). 다음은 브라우저 구현을위한 빠르고 더러운 테스트 페이지 (아래)입니다 (매우 더럽고 테스트 중에 생성되지 않으므로 최소한의 피드백 만 제공하며 스크립트 시간 제한을 무시 함). 테스트 사이에 새로 고침을 권장합니다. 그렇지 않으면 반복 테스트에서 FF (적어도)가 느려집니다.

Array # concat을 사용하는 상당히 복잡한 버전은 1,000에서 2,000 개의 요소 배열보다 FF에서 바로 초기화하는 것보다 빠릅니다. 그러나 Chrome의 V8 엔진에서는 straight init가 매번 승리합니다.

테스트 페이지는 다음과 같습니다 ( 라이브 사본 ).

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Zero Init Test Page</title>
<style type='text/css'>
body {
    font-family:    sans-serif;
}
#log p {
    margin:     0;
    padding:    0;
}
.error {
    color:      red;
}
.winner {
    color:      green;
    font-weight:    bold;
}
</style>
<script type='text/javascript' src='prototype-1.6.0.3.js'></script>
<script type='text/javascript'>
var testdefs = {
    'downpre':  {
        total:  0,
        desc:   "Count down, pre-decrement",
        func:   makeWithCountDownPre
    },
    'downpost': {
        total:  0,
        desc:   "Count down, post-decrement",
        func:   makeWithCountDownPost
    },
    'up':       {
        total:  0,
        desc:   "Count up (normal)",
        func:   makeWithCountUp
    },
    'downandup':  {
        total:  0,
        desc:   "Count down (for loop) and up (for filling)",
        func:   makeWithCountDownArrayUp
    },
    'concat':   {
        total:  0,
        desc:   "Concat",
        func:   makeWithConcat
    }
};

document.observe('dom:loaded', function() {
    var markup, defname;

    markup = "";
    for (defname in testdefs) {
        markup +=
            "<div><input type='checkbox' id='chk_" + defname + "' checked>" +
            "<label for='chk_" + defname + "'>" + testdefs[defname].desc + "</label></div>";
    }
    $('checkboxes').update(markup);
    $('btnTest').observe('click', btnTestClick);
});

function epoch() {
    return (new Date()).getTime();
}

function btnTestClick() {

    // Clear log
    $('log').update('Testing...');

    // Show running
    $('btnTest').disabled = true;

    // Run after a pause while the browser updates display
    btnTestClickPart2.defer();
}
function btnTestClickPart2() {

    try {
        runTests();
    }
    catch (e) {
        log("Exception: " + e);
    }

    // Re-enable the button; we don't yheidl
    $('btnTest').disabled = false;
}

function runTests() {
    var start, time, counter, length, defname, def, results, a, invalid, lowest, s;

    // Get loops and length
    s = $F('txtLoops');
    runcount = parseInt(s);
    if (isNaN(runcount) || runcount <= 0) {
        log("Invalid loops value '" + s + "'");
        return;
    }
    s = $F('txtLength');
    length = parseInt(s);
    if (isNaN(length) || length <= 0) {
        log("Invalid length value '" + s + "'");
        return;
    }

    // Clear log
    $('log').update('');

    // Do it
    for (counter = 0; counter <= runcount; ++counter) {

        for (defname in testdefs) {
            def = testdefs[defname];
            if ($('chk_' + defname).checked) {
                start = epoch();
                a = def.func(length);
                time = epoch() - start;
                if (counter == 0) {
                    // Don't count (warm up), but do check the algorithm works
                    invalid = validateResult(a, length);
                    if (invalid) {
                        log("<span class='error'>FAILURE</span> with def " + defname + ": " + invalid);
                        return;
                    }
                }
                else {
                    // Count this one
                    log("#" + counter + ": " + def.desc + ": " + time + "ms");
                    def.total += time;
                }
            }
        }
    }

    for (defname in testdefs) {
        def = testdefs[defname];
        if ($('chk_' + defname).checked) {
            def.avg = def.total / runcount;
            if (typeof lowest != 'number' || lowest > def.avg) {
                lowest = def.avg;
            }
        }
    }

    results =
        "<p>Results:" +
        "<br>Length: " + length +
        "<br>Loops: " + runcount +
        "</p>";
    for (defname in testdefs) {
        def = testdefs[defname];
        if ($('chk_' + defname).checked) {
            results += "<p" + (lowest == def.avg ? " class='winner'" : "") + ">" + def.desc + ", average time: " + def.avg + "ms</p>";
        }
    }
    results += "<hr>";
    $('log').insert({top: results});
}

function validateResult(a, length) {
    var n;

    if (a.length != length) {
        return "Length is wrong";
    }
    for (n = length - 1; n >= 0; --n) {
        if (a[n] != 0) {
            return "Index " + n + " is not zero";
        }
    }
    return undefined;
}

function makeWithCountDownPre(len) {
    var a;

    a = new Array(len);
    while (--len >= 0) {
        a[len] = 0;
    }
    return a;
}

function makeWithCountDownPost(len) {
    var a;

    a = new Array(len);
    while (len-- > 0) {
        a[len] = 0;
    }
    return a;
}

function makeWithCountUp(len) {
    var a, i;

    a = new Array(len);
    for (i = 0; i < len; ++i) {
        a[i] = 0;
    }
    return a;
}

function makeWithCountDownArrayUp(len) {
    var a, i;

    a = new Array(len);
    i = 0;
    while (--len >= 0) {
        a[i++] = 0;
    }
    return a;
}

function makeWithConcat(len) {
    var a, rem, currlen;

    if (len == 0) {
        return [];
    }
    a = [0];
    currlen = 1;
    while (currlen < len) {
        rem = len - currlen;
        if (rem < currlen) {
            a = a.concat(a.slice(0, rem));
        }
        else {
            a = a.concat(a);
        }
        currlen = a.length;
    }
    return a;
}

function log(msg) {
    $('log').appendChild(new Element('p').update(msg));
}
</script>
</head>
<body><div>
<label for='txtLength'>Length:</label><input type='text' id='txtLength' value='10000'>
<br><label for='txtLoops'>Loops:</label><input type='text' id='txtLoops' value='10'>
<div id='checkboxes'></div>
<br><input type='button' id='btnTest' value='Test'>
<hr>
<div id='log'></div>
</div></body>
</html>

답변

기본적으로 Uint8Array, Uint16Array그리고 Uint32Array클래스를 사용하면 복잡한 충전 기술을 필요가 없습니다, 단지 수행의 값으로 0을 유지 :

var ary = new Uint8Array(10);

배열의 모든 요소는 ary기본적으로 0입니다.