온보딩 커리큘럼 [배열, 반복문 + 확인문제]
배열
let arr = new Array();
let arr = [];
let fruits = ["사과", "오렌지", "자두"];
alert( fruits[0] ); // 사과
alert( fruits[1] ); // 오렌지
alert( fruits[2] ); // 자두
아래는 배열 끝에 무언가를 해주는 메서드입니다.
let fruits = ["사과", "오렌지", "배"];
alert( fruits.pop() ); // 배열에서 "배"를 제거하고 제거된 요소를 얼럿창에 띄웁니다.
alert( fruits ); // 사과,오렌지
let fruits = ["사과", "오렌지"];
fruits.push("배");
alert( fruits ); // 사과,오렌지,배
let fruits = ["사과", "오렌지", "배"];
alert( fruits.shift() ); // 배열에서 "사과"를 제거하고 제거된 요소를 얼럿창에 띄웁니다.
alert( fruits ); // 오렌지,배
let fruits = ["오렌지", "배"];
fruits.unshift('사과');
alert( fruits ); // 사과,오렌지,배
성능
fruits.shift(); // 배열 맨 앞의 요소를 빼줍니다.
-
인덱스가 0인 요소를 제거합니다.
-
모든 요소를 왼쪽으로 이동시킵니다. 이때 인덱스 1은 0, 2는 1로 변합니다.
-
length 프로퍼티 값을 갱신합니다.
그런데 배열에 요소가 많으면 요소가 이동하는 데 걸리는 시간이 길고 메모리 관련 연산도 많아집니다.
다차원배열
배열 역시 배열의 요소가 될 수 있습니다. 이런 배열을 가리켜 다차원 배열(multidimensional array)이라 부릅니다. 다차원 배열은 행렬을 저장하는 용도로 쓰입니다.
let matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
alert( matrix[1][1] ); // 5, 중심에 있는 요소
배열과 메서드
splice
splice는 삭제된 요소로 구성된 배열을 반환한다.
arr.splice(index[, deleteCount, elem1, ..., elemN])
let arr = ["I", "study", "JavaScript"];
arr.splice(1, 1); // 인덱스 1부터 요소 한 개를 제거
alert( arr ); // ["I", "JavaScript"]
요소 삭제 후 다른 요소로 교체
let arr = ["I", "study", "JavaScript", "right", "now"];
// 처음(0) 세 개(3)의 요소를 지우고, 이 자리를 다른 요소로 대체합니다.
arr.splice(0, 3, "Let's", "dance");
alert( arr ) // now ["Let's", "dance", "right", "now"]
splice 메서드의 deleteCount를 0으로 설정하면 요소를 제거하지 않으면서 새로운 요소를 추가할 수 있습니다
let arr = ["I", "study", "JavaScript"];
// 인덱스 2부터
// 0개의 요소를 삭제합니다.
// 그 후, "complex"와 "language"를 추가합니다.
arr.splice(2, 0, "complex", "language");
alert( arr ); // "I", "study", "complex", "language", "JavaScript"
let arr = [1, 2, 5];
// 인덱스 -1부터 (배열 끝에서부터 첫 번째 요소)
// 0개의 요소를 삭제하고
// 3과 4를 추가합니다.
arr.splice(-1, 0, 3, 4);
alert( arr ); // 1,2,3,4,5
slice
arr.slice([start], [end])
let arr = ["t", "e", "s", "t"];
alert( arr.slice(1, 3) ); // e,s (인덱스가 1인 요소부터 인덱스가 3인 요소까지를 복사(인덱스가 3인 요소는 제외))
alert( arr.slice(-2) ); // s,t (인덱스가 -2인 요소부터 제일 끝 요소까지를 복사)
concat
arr.concat(arg1, arg2...)
let arr = [1, 2];
// arr의 요소 모두와 [3,4]의 요소 모두를 한데 모은 새로운 배열이 만들어집니다.
alert( arr.concat([3, 4]) ); // 1,2,3,4
// arr의 요소 모두와 [3,4]의 요소 모두, [5,6]의 요소 모두를 모은 새로운 배열이 만들어집니다.
alert( arr.concat([3, 4], [5, 6]) ); // 1,2,3,4,5,6
// arr의 요소 모두와 [3,4]의 요소 모두, 5와 6을 한데 모은 새로운 배열이 만들어집니다.
alert( arr.concat([3, 4], 5, 6) ); // 1,2,3,4,5,6
concat 메서드는 제공받은 배열의 요소를 복사해 활용합니다. 객체가 인자로 넘어오면 (배열처럼 보이는 유사 배열 객체이더라도) 객체는 분해되지 않고 통으로 복사되어 더해집니다
let arr = [1, 2];
let arrayLike = {
0: "something",
length: 1
};
alert( arr.concat(arrayLike) ); // 1,2,[object Object]
그런데 인자로 받은 유사 배열 객체에 특수한 프로퍼티 Symbol.isConcatSpreadable이 있으면 concat은 이 객체를 배열처럼 취급합니다. 따라서 객체 전체가 아닌 객체 프로퍼티의 값이 더해집니다.
let arr = [1, 2];
let arrayLike = {
0: "something",
1: "else",
[Symbol.isConcatSpreadable]: true,
length: 2
};
alert( arr.concat(arrayLike) ); // 1,2,something,else
forEach로 반복 작업 하기.
주어진 함수를 배열 요소 각각에 대해 실행할 수 있게 해줍니다.
arr.forEach(function(item, index, array) {
// 요소에 무언가를 할 수 있습니다.
});
["Bilbo", "Gandalf", "Nazgul"].forEach((item, index, array) => {
alert(`${item} is at index ${index} in ${array}`);
});
인수로 넘겨준 함수의 반환값은 무시됩니다.
배열 탐색하기
indexOf, lastlndexOf, includes
-
arr.indexOf(item, from)는 인덱스 from부터 시작해 item(요소)을 찾습니다. 요소를 발견하면 해당 요소의 인덱스를 반환하고, 발견하지 못했으면 -1을 반환합니다.
-
arr.lastIndexOf(item, from)는 위 메서드와 동일한 기능을 하는데, 검색을 끝에서부터 시작한다는 점만 다릅니다.
-
arr.includes(item, from)는 인덱스 from부터 시작해 item이 있는지를 검색하는데, 해당하는 요소를 발견하면 true를 반환합니다.
let arr = [1, 0, false];
alert( arr.indexOf(0) ); // 1
alert( arr.indexOf(false) ); // 2
alert( arr.indexOf(null) ); // -1
alert( arr.includes(1) ); // true
위 메서드들은 요소를 찾을 때 완전 항등 연산자 === 을 사용한다는 점에 유의하시기 바랍니다. 보시는 바와 같이 false를 검색하면 정확히 false만을 검색하지, 0을 검색하진 않습니다.
const arr = [NaN];
alert( arr.indexOf(NaN) ); // -1 (완전 항등 비교 === 는 NaN엔 동작하지 않으므로 0이 출력되지 않습니다.)
alert( arr.includes(NaN) );// true (NaN의 여부를 확인하였습니다.)
find와 findIndex
특정조건에 부합하는 객체를 배열에서 찾을 수 있다.
find 메서드는 함수의 반환 값을 true로 만드는 단 하나의 요소를 찾습니다.
let result = arr.find(function(item, index, array) {
// true가 반환되면 반복이 멈추고 해당 요소를 반환합니다.
// 조건에 해당하는 요소가 없으면 undefined를 반환합니다.
});
요소 전체를 대상으로 함수가 순차적으로 호출됩니다.
-
item – 함수를 호출할 요소
-
index – 요소의 인덱스
-
array – 배열 자기 자신
함수가 참을 반환하면 탐색은 중단되고 해당 요소가 반환됩니다. 원하는 요소를 찾지 못했으면 undefined가 반환됩니다.
let users = [
{id: 1, name: "John"},
{id: 2, name: "Pete"},
{id: 3, name: "Mary"}
];
let user = users.find(item => item.id == 1);
alert(user.name); // John
filter
filter는 find와 문법이 유사하지만, 조건에 맞는 요소 전체를 담은 배열을 반환한다는 점에서 차이가 있습니다.
let results = arr.filter(function(item, index, array) {
// 조건을 충족하는 요소는 results에 순차적으로 더해집니다.
// 조건을 충족하는 요소가 하나도 없으면 빈 배열이 반환됩니다.
});
let users = [
{id: 1, name: "John"},
{id: 2, name: "Pete"},
{id: 3, name: "Mary"}
];
// 앞쪽 사용자 두 명을 반환합니다.
let someUsers = users.filter(item => item.id < 3);
alert(someUsers.length); // 2
배열을 변형하는 메서드
map
map은 배열 요소 전체를 대상으로 함수를 호출하고, 함수 호출 결과를 배열로 반환해줍니다.
let result = arr.map(function(item, index, array) {
// 요소 대신 새로운 값을 반환합니다.
});
let lengths = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length);
alert(lengths); // 5,7,6
sort(fn)
배열의 요소를 정렬해줍니다. 배열 자체가 변경됩니다.
메서드를 호출하면 재정렬 된 배열이 반환되는데, 이미 arr 자체가 수정되었기 때문에 반환 값은 잘 사용되지 않는 편입니다.
요소는 문자열로 취급되어 사전순으로 재 정렬된다.
숫자를 정렬하려면 기본 정렬 기준 대신 새로운 정렬 기준을 만들려면 arr.sort()에 새로운 함수를 넘겨줘야 합니다.
function compareNumeric(a, b) {
if (a > b) return 1;
if (a == b) return 0;
if (a < b) return -1;
}
let arr = [ 1, 2, 15 ];
arr.sort(compareNumeric);
alert(arr); // 1, 2, 15
화살표 함수를 이용한 정렬 함수
arr.sort( (a, b) => a - b );
reverse
arr의 요소를 역순으로 정렬시켜주는 메서드입니다.
let arr = [1, 2, 3, 4, 5];
arr.reverse();
alert( arr ); // 5,4,3,2,1
split과 join
이 메서드는 구분자(delimiter) delim을 기준으로 문자열을 쪼개줍니다.
let names = 'Bilbo, Gandalf, Nazgul';
let arr = names.split(', ');
for (let name of arr) {
alert( `${name}에게 보내는 메시지` ); // Bilbo에게 보내는 메시지
}
split 메서드는 두 번째 인수로 숫자를 받을 수 있습니다. 이 숫자는 배열의 길이를 제한해주므로 길이를 넘어서는 요소를 무시할 수 있습니다. 실무에서 자주 사용하는 기능은 아닙니다.
let arr = 'Bilbo, Gandalf, Nazgul, Saruman'.split(', ', 2);
alert(arr); // Bilbo, Gandalf
split(s)의 s를 빈 문자열로 지정하면 문자열을 글자 단위로 분리할 수 있습니다.
let str = "test";
alert( str.split('') ); // t,e,s,t
arr.join은 split과 반대 역할을 하는 메서드입니다. 인수 glue를 접착제처럼 사용해 배열 요소를 모두 합친 후 하나의 문자열을 만들어줍니다.
let arr = ['Bilbo', 'Gandalf', 'Nazgul'];
let str = arr.join(';'); // 배열 요소 모두를 ;를 사용해 하나의 문자열로 합칩니다.
alert( str ); // Bilbo;Gandalf;Nazgul
reduce와 reduceRight
reduce와 reduceRight는 배열을 기반으로 값 하나를 도출할 때 사용됩니다.
let value = arr.reduce(function(accumulator, item, index, array) {
// ...
}, [initial]);
-
accumulator – 이전 함수 호출의 결과. initial은 함수 최초 호출 시 사용되는 초깃값을 나타냄(옵션)
-
item – 현재 배열 요소
-
index – 요소의 위치
-
array – 배열
이전 함수 호출 결과는 다음 함수를 호출할 때 첫 번째 인수(previousValue)로 사용됩니다.
첫 번째 인수는 앞서 호출했던 함수들의 결과가 누적되어 저장되는 '누산기(accumulator)'라고 생각하면 됩니다. 마지막 함수까지 호출되면 이 값은 reduce의 반환 값이 됩니다
let arr = [1, 2, 3, 4, 5];
let result = arr.reduce((sum, current) => sum + current, 0);
alert(result); // 15
-
함수 최초 호출 시, reduce의 마지막 인수인 0(초깃값)이 sum에 할당됩니다. current엔 배열의 첫 번째 요소인 1이 할당됩니다. 따라서 함수의 결과는 1이 됩니다.
-
두 번째 호출 시, sum = 1 이고 여기에 배열의 두 번째 요소(2)가 더해지므로 결과는 3이 됩니다.
-
세 번째 호출 시, sum = 3 이고 여기에 배열의 다음 요소가 더해집니다. 이런 과정이 계속 이어집니다.
Array.isArray로 배열 여부 알아내기
alert(typeof {}); // object
alert(typeof []); // object
그런데 배열은 자주 사용되는 자료구조이기 때문에 배열인지 아닌지를 감별해내는 특별한 메서드가 있다면 아주 유용할 겁니다. Array.isArray(value)는 이럴 때 사용할 수 있는 유용한 메서드입니다. value가 배열이라면 true를, 배열이 아니라면 false를 반환해주죠.
Array.isArray() - JavaScript | MDN
Array.isArray() 메서드는 인자가 Array인지 판별합니다.
developer.mozilla.org
alert(Array.isArray({})); // false
alert(Array.isArray([])); // true
배열 메서드와 'thisArg'
함수를 호출하는 대부분의 배열 메서드(find, filter, map 등. sort는 제외)는 thisArg라는 매개변수를 옵션으로 받을 수 있습니다
arr.find(func, thisArg);
arr.filter(func, thisArg);
arr.map(func, thisArg);
// ...
// thisArg는 선택적으로 사용할 수 있는 마지막 인수입니다.
let army = {
minAge: 18,
maxAge: 27,
canJoin(user) {
return user.age >= this.minAge && user.age < this.maxAge;
}
};
let users = [
{age: 16},
{age: 20},
{age: 23},
{age: 30}
];
// army.canJoin 호출 시 참을 반환해주는 user를 찾음
let soldiers = users.filter(army.canJoin, army);
alert(soldiers.length); // 2
alert(soldiers[0].age); // 20
alert(soldiers[1].age); // 23
<배열 확인문제>
- 다음 배열들의 2번째 인덱스에 있는 값을 찾아보세요.
- [”1”, ”2”, ”3”, ”4”] → “3”
- [”사과”, “배”, “바나나”, “귤”, “감”] →”바나나”
- [52, 273, 32, 103, 57] → 32
- 다음 코드의 실행 결과를 예측해보세요.
const array = [1, 2, 3, 4]
console.log(array.length)
console.log(array.push(5))
// 4
// 5
3. 다음 표시된 함수들이 파괴적 처리를 하는지 비파괴적 처리를 하는지 구분해 맞는것에 O 표시하세요.
[파괴적 처리 / 비파괴적 처리]
const strA = "사과,배,바나나,귤"
//undefined
strA.split(",")
//(4)["사과","배","바나나","귤"]
strA
//"사과,배,바나나,귤"
[파괴적 처리 / 비파괴적 처리]
const arrayB = ["사과", "배", "바나나", "귤"]
// undefined
arrayB.push("감")
// 5
arrayB
// ["사과", "배", "바나나", "귤", "감"]
[파괴적 처리 / 비파괴적 처리]
const arrayC = [1,2,3,4,5]
// undefined
arrayC.map((x) => x*x)
// (5) [1,4,9,16,25]
arrayC
// (5)[1,2,3,4,5]
[파괴적 처리 / 비파괴적 처리]
const strD = " 여백이 포함된 메세지 "
// undefined
strD.trim()
// "여백이 포함된 메세지"
strD
// " 여백이 포함된 메세지 "
<반복문 확인문제>
1. 다음 프로그램의 실행 결과를 예측해보세요.
const array = ['사과', '배', '귤', '바나나']
console.log('# for in 반복문') //index를 반환한다.
for (const i in array) {
console.log(i)
}
// # for in 반복문
// 0
// 1
// 2
// 3
console.log('# for of 반복문')//요소를 반환한다.
for (const i of array) {
console.log(i)
}
// # for of 반복문
// 사과
// 배
// 귤
// 바나나
2. 다음 프로그램의 실행 결과를 예측해보세요. 혹시 오류가 발생한다면 어디를 수정해야 할까요?
const array = []
for (const i = 0; i < 3; i++) {
array.push((i + 1) * 3)
}
console.log(array)
// const는 상수이기 떄문에 i++가 되었을때 오류가 발생한다. let으로 변경하면
// [3, 6, 9] 가 출력된다.
3. 1부터 100까지의 숫자를 곱한 값을 계산하는 프로그램을 만들어보세요. 그리고 코드를 실행해 나온 결과를 확인해 보세요.
let output = 1
for (i = 1; i <= 100; i++) {
output *= i
}
console.log(`1~100의 숫자를 모두 곱하면 ${output}입니다.`)
//1~100의 숫자를 모두 곱하면 9.33262154439441e+157입니다.
4. 처음에는 조금 어려울 수 있겠지만, 활용 예제의 피라미드를 활용해서 다음과 같은 피라미드를 만들어보세요.
*
***
*****
*******
*****
***
*
let output = ''
const size = 5
for (let i = 1; i < 5; i++) {
for (let j = 5; j > i; j--) {
output += ' '
}
for (let k = 1; k < i * 2; k++) {
output += '*'
}
output += '\n'
}
for (let i = size - 1; i > 0; i--) {
for (let j = 6; j > i + 1; j--) {
output += ' '
}
for (let k = 0; k < 2 * i - 1; k++) {
output += '*'
}
output += '\n'
}
console.log(output)