먹고 기도하고 코딩하라

자바스크립트 Array 메소드 (기본) 간단 실험 본문

Javascript

자바스크립트 Array 메소드 (기본) 간단 실험

사과먹는사람 2020. 6. 20. 23:45
728x90
728x90

Array 메소드 몇 개도 실험해 보겠습니다.

여기서는 map, filter 같은 것들 말고 정말 간단하고 기초적인 슬라이싱, 데이터 삽입과 삭제 메소드를 다뤄보겠습니다.

 

 


 

let arr = ['Do', 'or', 'do', 'not.', 'There', 'is', 'no', 'try'];
console.log(arr.join(' '));	//Do or do not. There is no try
console.log(`after arr.join: ${arr}`);
//after arr.join: Do,or,do,not.,There,is,no,try

일단 join(token:String) 메소드를 보겠습니다. join 메소드는 token을 요소 사이사이에 넣어 연결해 String으로 만들어 반환합니다. join을 한 뒤에 원본 Array에는 변화가 없는 모습입니다.

 

 

arr.slice 메소드

console.log(arr.length);	//8
console.log(arr.slice());	
//["Do", "or", "do", "not.", "There", "is", "no", "try"]

이제 제가 좋아하는 slice(start:int, end:int) 메소드를 보겠습니다. slice 메소드는 start 인덱스부터 end 인덱스-1까지 배열 요소를 잘라 Array로 반환하는데요, 만약 end 인자 없이 start만 있다면 자동으로 slice(start, arr.length)로 처리되어 start 인덱스부터 배열 요소 끝까지 다 잘라옵니다. 하지만 그렇다고 원본 배열에 변화가 생기는 건 아니고, 원본 배열은 똑같습니다.

 

몇 가지 특징을 살펴보겠습니다.

우선 인자가 없을 때 slice 메소드는 원본 배열 자체를 고스란히 반환합니다. 그래서 slice 메소드는 배열을 그대로 복사하고 싶을 때 사용하기도 합니다. 왜냐하면 배열을 가리키는 변수는 사실 배열 자체를 담는 것이 아니라 Array를 가리키는 레퍼런스이거든요.

깊은 복사, 얕은 복사라고도 배웠는데 얕은 복사는 레퍼런스만 하나 더 생성해서 원래 있는 객체를 가리키는 것이고 깊은 복사는 객체 자체를 복사해서 추가로 생성한 레퍼런스가 그 새로운 객체를 가리키게 하는 것입니다.

글로 이해가 되지 않는다면 코드를 보여 드리겠습니다.

 

let arr = ['Do', 'or', 'do', 'not.', 'There', 'is', 'no', 'try'];
let arr2 = arr;
arr.push('- Yoda said.');
console.log(arr2);
//["Do", "or", "do", "not.", "There", "is", "no", "try", "- Yoda said."]

arr에 push를 했는데 arr2에도 똑같이 반영된 모습입니다. arr, arr2이 하나의 Array 객체를 가리키고 있기 때문입니다.

깊은 복사를 하려면 arr.slice()를 arr2에 할당해주면 됩니다.

let arr = ['Do', 'or', 'do', 'not.', 'There', 'is', 'no', 'try'];
let arr2 = arr.slice();
arr.push('- Yoda said.');
console.log(arr2);
//["Do", "or", "do", "not.", "There", "is", "no", "try"]

 

다시 본론으로 돌아가겠습니다.

 

console.log(arr.slice(3));	//["not.", "There", "is", "no", "try"]
console.log(arr.slice(3, 3));	//[] 빈 배열
console.log(arr.slice(3, 6));	//["not.", "There", "is"]
console.log(arr.slice(3, -3));	//["not.", "There"]
console.log(arr.slice(3, -6));	//[] 빈 배열
console.log(arr.slice(-3));	//["is", "no", "try"]
console.log(arr.slice(-3, -1));	//["is", "no"]
console.log(arr.slice(-3, -3);	//[] 빈 배열
console.log(arr.slice(-3, -6));	//[] 빈 배열
console.log(arr.slice(-3, 3));	//[] 빈 배열
console.log(arr.slice(-3, 6));	//["is"]
console.log(`after arr.slice: ${arr}`);
//after arr.slice: Do,or,do,not.,There,is,no,try

start 인자가 양수 1개만 주어질 때 slice 메소드는 slice(start, arr.length)처럼 행동합니다. slice(3)을 줬더니 slice(3, 8)을 한 것처럼 5개 요소가 담긴 배열이 출력됩니다.

start 인자가 음수 1개일 때 slice 메소드는 slice(arr.length+start, arr.length)처럼 행동합니다. 음수 인덱싱을 할 때, -1이 가장 마지막 요소이고 -length가 가장 첫 번째 요소임을 기억하세요. slice(-3)을 줬더니 slice(8-3, 8), 즉 slice(5, 8)인 것처럼 3개 요소가 담긴 배열이 출력됩니다.

 

start와 end의 값이 같다면 빈 배열이 출력됩니다.

start와 end가 모두 양수고 start가 더 크다면 빈 배열이 출력됩니다.

start와 end가 모두 양수고 end가 더 크다면 일반적으로 예상하는 결과가 출력됩니다. (3, 6)일 때 3번부터 5번까지 3개의 요소를 배열로 가져와 반환합니다.

start 인자가 양수일 때 end 인자가 음수라면 start 인자가 몇 번이냐에 따라 결과가 조금 달라집니다. 가장 마지막 요소는 length-1이자 -1으로 접근할 수 있습니다. 즉 이 배열에서 -3은 length-3의 값인 5나 마찬가지인데요. (3, 5)가 되니까 3번부터 4번까지 2개 요소를 배열로 가져와 반환합니다. 하지만 -6은 2입니다. (3, 2)와 같아지므로 빈 배열이 반환됩니다.

 

start와 end 모두 음수일 때는 무조건 양수로 바꿔서 생각해 보세요. (-3, -1)은 (5, 7)이므로 말이 됩니다. 5번부터 6번까지 2개 요소가 배열로 담겨 나오죠. (-3, -3)은 (5, 5)이므로 둘의 값이 같아서 아무것도 출력되지 않습니다. 같은 논리로 (-3, -6)은 (5, 2)이므로 start가 end보다 더 큰 숫자라 아무것도 출력되지 않습니다.

start만 음수이고 end가 양수일 때도 숫자 봐서 따지게 됩니다. (-3, 3)의 경우 (5, 3)과 똑같습니다. 아무것도 출력되지 않죠. 하지만 (-3, 6)은 (5, 6)이므로 5번 자리의 ["is"]가 단독으로 배열에 담겨 나오게 됩니다.

자 이제 신나게 슬라이싱한 결과를 보면... 변한 게 아무것도 없습니다. slice는 원본 배열에 변화를 주지 않습니다.

 

빈 배열이 출력되는 케이스

  • end = 0
  • start = end
  • 음수 인덱싱일 때는 양수로 바꿔 계산하기 -> 그 때 start > end

 

 

arr.splice 메소드

arr = ['Kelly', 'has', 'some', 'money'];
console.log(arr.splice(2, 2, ['money', 'and', 'bread']));	//["some", "money"]
console.log(`after arr.splice: ${arr}`);
//after arr.splice: Kelly,has,money,and,bread
console.log(arr);	//["Kelly", "has", Array(3)]

splice 메소드는 slice와는 좀 다른 방식으로 동작합니다. slice가 그냥 원본 배열을 변경하지 않고 일부만 복사해서 잘라내는 방식이라면 splice는 원본 배열의 일부를 잘라내고 그 자리에 뭔가 삽입하는 것을 목표로 합니다.

splice(start:int, count:int, element:any)에서 start는 삭제할 요소가 시작하는 인덱스, count가 그 자리부터 삭제할 요소의 개수, 그리고 element가 그 삭제한 자리에 들어갈 요소입니다. 이 요소는 primitive 값, 객체, 위의 예처럼 배열 등 거의 뭐든 될 수 있습니다.

splice는 삭제한 요소를 Array에 담아 반환합니다.

2번째 문장을 볼까요? splice(2, 2, ['money', 'and', 'bread'])입니다. 2번부터 2개 요소를 자르고 그 자리에 ['money', 'and', 'bread']를 넣겠다는 뜻입니다. Kelly에게 이제 빵도 생겼습니다.

그런데 이렇게 배열째로 element를 주는 것예상하지 못한 결과를 부를 수도 있습니다. 다음의 예시를 살펴보겠습니다.

 

arr = ['Kelly', 'has', 'some', 'money'];
console.log(arr.splice(3, 1, ['money', 'and', 'bread']));	//["money"]
console.log(arr);	//["Kelly", "has", "some", Array(3)]
console.log(arr.pop());	//["money", "and", "bread"]
console.log(arr);	//["Kelly", "has", "some"]

arr = ['Kelly', 'has', 'some', 'money'];
console.log(arr.splice(3, 1, 'money', 'and', 'bread'));	//["money"]
console.log(arr);	//["Kelly", "has", "some", "money", "and", "bread"]
console.log(arr.pop());	//bread
console.log(arr);	//["Kelly", "has", "some", "money", "and"]

splice 메소드가 비슷해보이는데 무슨 차이가 있을까요?

첫 번째 splice는 element를 배열 형태로 줬고, 두 번째 splice는 element를 문자열 3개를 따로따로 줬습니다. 그리고 arr을 출력해보면 각자 다른 결과가 나옵니다.

첫 번째 splice의 결과로 요소 3개짜리 Array가 그대로 arr에 포함된 것을 볼 수 있습니다. 쉽게 말하면 2차원 배열이 된 겁니다. 반면 두 번째 splice의 결과는 문자열 요소 6개짜리 Array입니다.

아래서 살펴볼 메소드를 조금 스포일러하자면 pop()은 배열의 가장 마지막 요소를 반환하는 메소드인데요, 첫 번째 pop()은 마지막 요소인 Array(3)을 그대로 뽑는 반면 두 번째 pop()은 마지막 요소인 문자열 bread만 뽑습니다.

 

다시 위의 예제로 돌아가보겠습니다.

console.log(arr);	//["Kelly", "has", Array(3)]

console.log(arr.splice(-1, 1));	//[Array(3)] 0: ["money", "and", "bread"]
console.log(`after arr.splice: ${arr}`);
//after arr.splice: Kelly,has

console.log(arr.splice(2, 0, 'some', 'bread'));	//[] (빈 배열)
console.log(`after arr.splice: ${arr}`);
//after arr.splice: Kelly,has,some,bread

splice를 했을 때 element 없이 start와 count만 적을 수도 있는데, 그렇게 되면 count만큼 원본 배열에서 삭제하고 나서 삽입을 따로 하지 않는 것입니다. 쉽게 말해 pop()이나 shift()로 접근하기 어려운 배열 중간 요소를 hole 없이 삭제하고 싶을 때 써먹기 좋다는 것이죠. 굳이 다른 데이터를 배열에 넣어줄 필요가 없습니다.

한편 splice의 count로 0을 줘도 됩니다. 궁금해서 음수를 줘봤는데 빈 배열이 나오는 건 똑같네요. 요소 삭제 없이 그냥 데이터를 중간에 끼워넣고 싶다면 count를 0으로 주면 됩니다.

눈치 채셨겠지만 start로 음수를 줘도 됩니다.

정리하자면 splice 메소드는 결과로 잘라낸 요소를 Array로 담아 반환하고, 실제 요소에 변화를 줍니다.

 

splice로 할 수 있는 일들

  • 배열의 중간 요소를 삭제하고 그 자리를 다른 요소로 대체하기
  • 배열의 중간 요소를 hole 없이 삭제하기
  • 삭제 없이 배열의 중간에 데이터 삽입하기

 

어려운 슬라이싱이 끝났네요! 이 뒤에 살펴볼 것들은 비교적 쉬운 것들입니다.

 

 

데이터 삭제/삽입 메소드

console.log(arr.pop());	//bread
console.log(`after arr.pop: ${arr}`);	
//after arr.pop: Kelly,has,some
console.log(arr.push('bread', 'and', 'water'));	//6
console.log(`after arr.push: ${arr}`);
//after arr.push: Kelly,has,some,bread,and,water

push, pop부터 보겠습니다.

pop()배열의 마지막 요소를 반환하며 그것을 배열에서 삭제합니다. 아까 splice 예제에서 살펴봤던 것처럼 그것의 데이터 타입이 무엇이든지 가리지 않고 반환을 합니다. pop 메소드는 만약 배열이 빈 배열이라면 undefined를 반환합니다.

push(x)배열의 마지막에 매개변수로 들어온 것을 삽입하며, 삽입 후 총 배열 길이(int)를 반환합니다. 

참고로 push 매개변수로 한 개만 줄 수 있는 건 아니고, 4번 문장처럼 여러 개를 한꺼번에 줄 수도 있습니다. 물론 배열로 줄 수도 있죠. 아래서 살펴볼 unshift 역시 마찬가지입니다. (이 경우에는 배열 자체가 요소 1개가 되는 걸 원치 않는다면 까서 넣긴 해야됩니다)

 

데이터 타입에 객체도 들어갈 수 있다는 것을 보여드리겠습니다. Kelly의 친구 Bob을 만들어서 push 해보겠습니다.

const Bob = {age: '24', major: 'CS'};
console.log(arr.push(Bob));	//7
console.log(JSON.stringify(arr));
//["Kelly","has","some","bread","and","water",{"age":"24","major":"CS"}]
console.log(arr[6].age);
console.log(arr.pop());	//{age: "24", major: "CS"}
console.log(arr);	//["Kelly", "has", "some", "bread", "and", "water"]

만약 JSON 객체를 아직 모르는 분이라면 3번 문장 때문에 혼란이 올 수 있을 텐데, 지금은 JSON.stringify(object)가 객체나 배열을 문자열로 바꿔준다는 것만 알고 계시면 됩니다. console.log(arr)을 했을 때 정상적으로 결과가 잘 보이지 않아서({Object}로 나옵니다) 출력을 위해 임의로 문자화시켜준 것입니다.

안타깝게도 이미 배열 요소로 들어간 이상 Bob이라는 이름으로 접근은 불가합니다. 대신 배열 요소 인덱스로 접근해 .age, .major 하는 식으로 프로퍼티 값을 볼 수는 있습니다.

객체도 넣을 수 있는 것 보셨죠? 이제 친구는 pop해서 다시 데려가겠습니다.

 

console.log(arr.shift());	//Kelly
console.log(`after arr.shift: ${arr}`);
//after arr.shift: has,some,bread,and,water
console.log(arr.unshift('Hey,', 'Kelly'));	//7
console.log(`after arr.unshift: ${arr}`);
//after arr.unshift: Hey,,Kelly,has,some,bread,and,water

unshift, shift도 마찬가지입니다. 뭔가 이름 때문에 unshift가 저는 삭제하는 거라고 생각했는데 알고 보니 shift가 삭제하는 거였습니다. 헷갈리는 분들은 기억하고 가세요~

shift()배열의 첫 번째 요소를 반환하며 그것을 배열에서 삭제합니다. pop처럼 데이터 타입이 무엇이든 상관하지 않고 반환합니다. 배열이 빈 배열이라면 undefined를 반환합니다.

unshift(x)배열의 맨 앞에 매개변수로 들어온 것을 삽입하며, 삽입 후 총 배열 길이(int)를 반환합니다.

 

push, pop은 배열의 맨 마지막,

unshift, shift는 배열의 맨 처음에 요소를 삽입하고 삭제하는 것임을 기억하세요!

 

 

기본 메소드는 여기까지 살펴보겠습니다!

고맙습니다. ^^

 

 

728x90
반응형
Comments