JavaScript 배열과 정렬 사용하기
예제 기준 · JavaScript : 표준 내장 Array·String 메서드 (별도 라이브러리 없음)
목록 화면에서는 배열의 값을 변환하거나 조건에 맞는 항목을 찾고 정렬하는 작업이 자주 필요합니다. map·filter로 배열을 가공하고, find·some·every로 조건을 확인한 뒤 숫자와 객체 배열을 sort로 정렬해 보겠습니다.
각 절의 버튼 코드는 해당 예제의 HTML 요소가 만들어진 뒤 독립적으로 사용합니다. 다음 공통 함수를 함께 두면 결과는 pre#result, 처리 안내는 #statusMessage에 표시됩니다.
function show(value) {
document.getElementById('result').textContent = String(value);
}
function message(value) {
document.getElementById('statusMessage').textContent = value;
}
map()과 filter() 사용하기
map()은 각 요소를 콜백 함수의 반환값으로 변환한 새 배열을 만듭니다. filter()는 조건을 만족하는 요소만 담은 새 배열을 만듭니다. 원본 [1, 2, 3, 4, 5]에 두 처리를 각각 적용합니다.
‘각 값 × 2 (map)’ 버튼 #map은 값을 두 배로, ‘짝수만 선택 (filter)’ 버튼 #filter는 value % 2 === 0인 요소만 추출합니다. filter는 map 결과가 아닌 원본에서 짝수를 고릅니다.
const original = [1, 2, 3, 4, 5];
document.getElementById('map').addEventListener('click', function () {
const result = original.map(function (value) {
return value * 2;
});
show('원본: ' + JSON.stringify(original) + '\nmap 결과: ' + JSON.stringify(result));
message('각 원소를 변환한 새 배열입니다.');
});
document.getElementById('filter').addEventListener('click', function () {
const result = original.filter(function (value) {
return value % 2 === 0;
});
show('원본: ' + JSON.stringify(original) + '\nfilter 결과: ' + JSON.stringify(result));
message('조건을 만족한 원소로 구성한 새 배열입니다.');
});
show('원본: ' + JSON.stringify(original));
map 버튼을 누르면 다음과 같이 표시됩니다.
원본: [1,2,3,4,5]
map 결과: [2,4,6,8,10]
filter 버튼은 같은 결과 영역을 다음 내용으로 바꿉니다.
원본: [1,2,3,4,5]
filter 결과: [2,4]
두 메서드 모두 새 배열을 반환하며 위 코드의 원본은 바뀌지 않습니다. filter 조건에 맞는 요소가 없으면 빈 배열 []이 됩니다. 다만 콜백 안에서 원본 배열이나 객체의 속성을 직접 수정하면 그 변경까지 막아 주는 것은 아닙니다.
find(), some(), every() 사용하기
find()는 첫 번째 일치 요소를 반환하며 없으면 undefined를 반환합니다. some()은 하나라도 조건을 만족하는지, every()는 모든 요소가 만족하는지를 boolean으로 반환합니다.
원본은 [10, 20, 30]입니다. ‘기준값 이상 검색’ 입력란 #minimum의 초기값은 20이며, ‘조건 검색’ 버튼 #run을 누르면 value >= minimum을 세 메서드에 동일하게 적용합니다.
const original = [10, 20, 30];
document.getElementById('run').addEventListener('click', function () {
const input = document.getElementById('minimum');
if (input.value === '' || !Number.isFinite(input.valueAsNumber)) {
message('유효한 숫자를 입력하세요.');
return;
}
const minimum = input.valueAsNumber;
function matches(value) {
return value >= minimum;
}
const found = original.find(matches);
const some = original.some(matches);
const every = original.every(matches);
show('원본: ' + JSON.stringify(original) + '\nfind: ' + String(found)
+ '\nsome: ' + some + '\nevery: ' + every);
message('find는 첫 일치값 또는 undefined, some/every는 boolean입니다.');
});
show('원본: ' + JSON.stringify(original));
원본: [10,20,30]
find: 20
some: true
every: false
20 이상인 첫 요소는 20이고, 20·30이 조건에 맞으므로 some은 true입니다. 10은 조건을 만족하지 않아 every는 false가 됩니다. 기준값에 따른 결과는 다음과 같습니다.
| 기준값 | find | some | every |
|---|---|---|---|
| 10 | 10 | true | true |
| 20 | 20 | true | false |
| 40 | undefined | false | false |
입력란이 비어 있거나 유효한 숫자가 아니면 ‘유효한 숫자를 입력하세요.’라고 안내하고 기존 결과는 유지합니다. 실행 예제에는 find 지원 여부를 확인해 미지원 브라우저의 입력과 버튼을 비활성화하는 처리도 있습니다. 위 코드는 find를 지원하는 환경의 검색 부분입니다.
find는 배열 위치가 아닌 요소 자체를 반환합니다. 숫자 0도 유효한 검색 결과가 될 수 있으므로 값이 참인지로 검색 성공 여부를 판단하지 않습니다. 이 숫자 배열에서는 undefined와 직접 비교하면 됩니다.
숫자 배열 정렬하기
비교 함수 없이 sort()를 호출하면 요소를 문자열로 비교합니다. 정확히는 UTF-16 코드 단위 순서이므로 숫자의 크기순과 다릅니다. 예를 들어 100은 문자열 비교에서 2보다 앞에 옵니다. 비교를 위해 문자열로 취급할 뿐 배열의 숫자를 문자열로 바꾸지는 않습니다.
원본은 [10, 2, 100, 1, 20]입니다. ‘기본 sort’ #default, ‘숫자 오름차순’ #ascending, ‘숫자 내림차순’ #descending 버튼은 원본을 slice()로 복사한 뒤 정렬합니다.
const original = [10, 2, 100, 1, 20];
function display(result) {
show('원본: ' + JSON.stringify(original) + '\n복사본 정렬: ' + JSON.stringify(result));
message('sort는 대상 배열을 변경하므로 원본을 slice()로 복사했습니다.');
}
document.getElementById('default').addEventListener('click', function () {
const result = original.slice();
result.sort();
display(result);
});
document.getElementById('ascending').addEventListener('click', function () {
const result = original.slice();
result.sort(function (a, b) {
return a - b;
});
display(result);
});
document.getElementById('descending').addEventListener('click', function () {
const result = original.slice();
result.sort(function (a, b) {
return b - a;
});
display(result);
});
show('원본: ' + JSON.stringify(original));
오름차순의 비교 함수는 a - b, 내림차순은 b - a를 반환합니다. 음수는 a를 b보다 앞에, 양수는 뒤에 놓으라는 뜻이며 0은 같은 순위라는 뜻입니다. true·false 대신 이 비교 결과를 반환해야 합니다.
| 버튼 | 복사본 정렬 결과 |
|---|---|
| 기본 sort | [1,10,100,2,20] |
| 숫자 오름차순 | [1,2,10,20,100] |
| 숫자 내림차순 | [100,20,10,2,1] |
화면에는 ‘원본’과 ‘복사본 정렬’이 함께 표시됩니다. 어느 버튼을 눌러도 original은 처음 순서 그대로입니다. sort()가 원본을 보존해서가 아니라 복사한 result만 정렬했기 때문입니다.
객체 배열 정렬하기
객체는 어떤 property를 기준으로 비교할지 명확히 정해야 합니다. 다음 데이터는 Orange의 quantity가 10, Apple이 3, Banana가 5입니다. ‘수량 오름차순’ 버튼 #quantity는 숫자 quantity를 빼고, ‘이름 오름차순’ 버튼 #name은 문자열 name을 비교합니다.
const original = [
{
name: 'Orange',
quantity: 10
},
{
name: 'Apple',
quantity: 3
},
{
name: 'Banana',
quantity: 5
}
];
function display(result) {
show('원본:\n' + JSON.stringify(original, null, 2) + '\n정렬 결과:\n' + JSON.stringify(result, null, 2));
message('복사한 배열의 순서만 변경합니다.');
}
document.getElementById('quantity').addEventListener('click', function () {
const result = original.slice();
result.sort(function (a, b) {
return a.quantity - b.quantity;
});
display(result);
});
document.getElementById('name').addEventListener('click', function () {
const result = original.slice();
result.sort(function (a, b) {
return a.name.localeCompare(b.name, 'en');
});
display(result);
});
show('원본:\n' + JSON.stringify(original, null, 2));
원본은 Orange → Apple → Banana 순서입니다. 이 데이터에서는 수량 오름차순과 이름 오름차순 모두 Apple(3) → Banana(5) → Orange(10)이 됩니다. 결과가 같아도 비교하는 property와 비교 방식은 다릅니다. 화면에는 원본과 정렬 결과의 객체 전체가 들여쓰기된 JSON으로 표시됩니다.
a.name.localeCompare(b.name, 'en')은 영어 로케일 기준의 문자열 순서를 비교합니다. 숫자는 뺄셈으로, 이름은 localeCompare()로 비교하며 한글 이름은 요구하는 정렬 기준에 따라 'ko' 같은 로케일을 선택할 수 있습니다. 반환값은 음수·0·양수로 판단하고 항상 -1 또는 1이라고 가정하지 않습니다.
객체 배열을 내림차순으로 정렬하기
다음은 같은 데이터로 내림차순을 만드는 별도 응용 코드입니다. 위 실행 예제에는 오름차순 버튼 두 개만 있습니다. 수량은 b에서 a를 빼고, 이름은 localeCompare()의 비교 순서를 뒤집습니다.
const original = [
{ name: 'Orange', quantity: 10 },
{ name: 'Apple', quantity: 3 },
{ name: 'Banana', quantity: 5 }
];
const byQuantity = original.slice();
byQuantity.sort(function (a, b) {
return b.quantity - a.quantity;
});
const byName = original.slice();
byName.sort(function (a, b) {
return b.name.localeCompare(a.name, 'en');
});
console.log(byQuantity);
console.log(byName);
이 데이터에서는 두 결과 모두 Orange(10) → Banana(5) → Apple(3) 순서입니다. original의 순서는 유지됩니다.
원본 배열을 변경하지 않고 정렬하기
sort()는 호출 대상 배열을 직접 변경하고 같은 배열을 반환합니다. 반환값을 다른 변수에 담아도 복사본이 생기지는 않습니다. 다음 독립 코드는 원본을 직접 정렬하는 경우입니다.
const original = [10, 2, 100, 1, 20];
const sorted = original.sort(function (a, b) {
return a - b;
});
console.log(original); // [1, 2, 10, 20, 100]
console.log(sorted === original); // true
원본 순서를 보존하려면 실행 예제처럼 복사와 정렬을 두 단계로 나눕니다.
const original = [10, 2, 100, 1, 20];
const sorted = original.slice();
sorted.sort(function (a, b) {
return a - b;
});
console.log(original); // [10, 2, 100, 1, 20]
console.log(sorted); // [1, 2, 10, 20, 100]
console.log(sorted === original); // false
slice()는 얕은 복사입니다. 객체 배열에서 복사본의 순서만 바꾸면 원본 순서는 유지되지만, 두 배열의 요소는 같은 객체를 참조합니다. 복사본에서 객체의 quantity를 수정하면 원본에서 읽는 quantity도 바뀝니다. 새 배열을 만드는 것과 내부 객체까지 복제하는 것은 구분해야 합니다.
목적에 맞는 배열 메서드 선택하기
| 메서드 | 목적 | 반환값 |
|---|---|---|
| map() | 각 요소의 값 변환 | 변환 결과를 담은 새 배열 |
| filter() | 조건에 맞는 요소 추출 | 일치 요소를 담은 새 배열 |
| find() | 첫 번째 일치 요소 검색 | 요소 또는 undefined |
| some() | 하나라도 조건에 맞는지 확인 | boolean |
| every() | 모든 요소가 조건에 맞는지 확인 | boolean |
| sort() | 요소의 순서 변경 | 정렬된 동일 배열 |
변환·필터링·검색·조건 검사·정렬은 목적과 반환값이 다릅니다. 작업 단계를 변수로 나누고, 정렬 전에 원본 순서를 바꿔도 되는지 확인하면 결과를 이해하기 쉽습니다.
