[JS] Array Functions

위영민(Victor)·2021년 8월 4일
0

개요

JS Array Object 는 아주 많은 메소드들을 존재하지만, 그 중 평소에 자주 사용하는 메소드들을 정리한다.

본론

📍 Array, 추가,삭제,반복작업

  • Array.prototype.splice()
  • Array.prototype.slice()
  • Array.prototype.concat()
  • Array.prototype.forEach()
{
  /**
   * Array.prototype.splice() 💡
   * - 배열 요소의 추가,삭제,교체 모두 가능
   * - 삭제된 요소로 이뤄진 배열을 따로 반환해준다.
   * - 음수 인덱싱도 가능함
   *
   * [기본 문법]
   * arr.splice(index[, deleteCount, elem1, ..., elemN])
   */
  let arr = ["I", "Love", "Me"];
  console.log(arr); // [ 'I', 'Love', 'Me' ]
  let removeData = arr.splice(2, 1, "You");
  console.log(removeData); // [ 'Me' ]
  console.log(arr); // [ 'I', 'Love', 'You' ]
  console.clear();

  /**
   * Array.prototype.slice() 💡
   * - 시작인덱스 ~ 끝인덱스-1 까지의 원본 배열을 복사한 새로운 배열을 반환
   * - 역시, 음수 인덱싱 가능(이 때는 음수 인덱스 ~ 끝까지)
   * - 문자열 메소드인 String.prototype.slice() 와 유사하게 동작
   * - 서브 문자열(substring) 대신, 서브 배열(subarray)을 반환한다는 점이 다름
   * - arr.slice() 처럼, 인수를 하나도 넘기지 않기도 함 ( 많이 사용 ) 🔍
   * - 이 방식은, "새로운 배열"을 생성하는 방법 중에 하나 이므로 자주 사용
   *
   * [기본 문법]
   * arr.slice([start], [end])
   */
  let arr2 = ["1", "2", "3", "4"];
  console.log(arr2.slice(1, 3)); // [ '2', '3' ]
  console.log(arr2.slice(-2)); // [ '3', '4' ]
  console.clear();

  /**
   * Array.prototype.concat() 💡
   * - 기존 배열의 요소를 사용해, "새로운 배열"을 만들거나, 기존 배열에 요소를 추가하고자 할 때 사용
   *
   * [기본 문법]
   * - arr.concat(arg1, arg2...)
   * - 인수에는 "배열"이나 "값"이 올 수 있다. 갯수 제한은 없다.
   * - 인수로 "객체"가 넘어오면(유사 배열객체라도), 객체는 "분해 되지 않고 통으로 복사"되어 더해짐 🔍
   * - 그런데, 유사 배열 객체에 "특수한 프로퍼티", "Symbol.isConcatSpreadable" 이 존재하면, concat 은 이 객체를 "배열처럼 취급"
   * - 따라서, 객체 프로퍼티의 값이 더해진다.
   *
   * [참고]
   * SymbolConstructor.isConcatSpreadable: typeof Symbol.isConcatSpreadable
   * - A Boolean value that if true indicates that an object should flatten to its array elements by Array.prototype.concat.
   */
  let arr3 = [1, 2];
  let arrayLike = {
    0: "something",
    length: 1,
  };
  console.log(arr3.concat(arrayLike)); // [ 1, 2, { '0': 'something', length: 1 } ]

  let arr4 = [1, 2];
  let arrayLike2 = {
    0: "something",
    1: "else",
    [Symbol.isConcatSpreadable]: true,
    length: 2,
  };
  console.log(arr4.concat(arrayLike2)); // [ 1, 2, 'something', 'else' ]
  console.clear();

  /**
   * forEach 로 반복작업 하기
   * - 주어진 함수를, 배열 요소 각각에 대해 실행할 수 있게 해준다.
   */
  [1, 2, 3, 4, 5].forEach((el, idx) => {
    console.log(el, idx);
  });
  // 1 0
  // 2 1
  // 3 2
  // 4 3
  // 5 4
  console.clear();
}

📍 Array 탐색

  • Array.prototype.indexOf()
  • Array.prototype.lastIndexOf()
  • Array.prototype.includes()
  • Array.prototype.find()
  • Array.prototype.findIndex()
  • Array.prototype.filter()
{
  /**
   * 배열 탐색
   * - Array.prototype.indexOf(item, from) : 배열에 앞에서부터, 해당 요소를 찾으면, 해당 요소의 인덱스를 반환, 없으면 -1 💡
   * - Array.prototype.lastIndexOf(item, from) : 배열에 뒤에서부터, 해당 요소를 찾으면, 해당 요소의 인덱스 반환, 없으면 -1 💡
   * - Array.prototype.includes(item, from) : 해당 요소를 발견하면 true, 없으면 false 💡
   *
   * 같은 이름을 가진 문자열 메소드들과 문법이 동일, 하는 일도 동일 🔍
   * 위에 세 가지 메소드 모두 "항등 연산자(===)"를 사용한다는 점에 유의할 것 🔍
   * 그렇기 때문에, 0 과 false 같은 것을 구분한다. 🔍
   *
   * 요소의 위치를 정확히 알고 싶은 것이 X
   * 배열 내 존재하는지 여부만을 확인하고 싶다 => array.includes 를 사용함
   * - includes 는 NaN 도 판단한다.
   */

  let arr1 = [1, 0, false, 1];
  console.log(arr1.indexOf(0)); // 1
  console.log(arr1.indexOf(false)); // 2
  console.log(arr1.indexOf(null)); // -1
  console.log(arr1.lastIndexOf(1)); // 3
  console.log(arr1.includes(1)); // true
  console.clear();

  let arr2 = [NaN];
  console.log(arr2.indexOf(NaN)); // -1
  console.log(arr2.includes(NaN)); // true
  console.clear();
  /**
	 * "객체"로 이뤄진 배열이 있을 때, "특정 조건"에 부합하는 객체를 배열 내에서 찾고 싶을 경우
   * - Array.prototype.find(function(item, index, array)) : 
   * - Array.prototype.findIndex(function(item, index, array)) : 
	 * 
	 * [기본 문법]
	 * let result = arr.find(function(item, index, array) {
  		true가 반환되면 반복이 멈추고 해당 요소를 반환합니다.
  		조건에 해당하는 요소가 없으면 undefined를 반환합니다.
	 }

	 * 실무에서 객체로 구성된 배열을 다뤄야할 일이 많아서, find 메소드 활용법을 알아두면 좋다.
	 * findIndex 는 find 와 동일하나, 조건에 맞는 요소의 "인덱스"를 반환, 없으면 -1 반환
   */
  let users = [
    { id: 1, name: "May" },
    { id: 2, name: "Jun" },
    { id: 3, name: "July" },
  ];

  let user = users.find((item) => item.id === 2);
  let userIdx = users.findIndex((item) => item.id === 3);
  console.log(user); // { id: 2, name: 'Jun' }
  console.log(userIdx); // 2
  console.clear();

  /**
	 * 조건에 맞는 원소 하나만이 아니라, "여러 개"를 반환받길 웒는 경우
	 * - Array.prototype.filter()
	 * 
	 * let results = arr.filter(function(item, index, array) {
  		// 조건을 충족하는 요소는 results에 순차적으로 더해집니다.
  		// 조건을 충족하는 요소가 하나도 없으면 빈 배열이 반환됩니다.
		});
	*/

  let someUsers = users.filter((item) => item.id < 3);
  console.log(someUsers); // [ { id: 1, name: 'May' }, { id: 2, name: 'Jun' } ]
}

📍 Array 변형

  • Array.prototype.map()
  • Array.prototype.sort()
  • Array.prototype.reverse()
  • Array.prototype.split()
  • Array.prototype.join()
  • Array.prototype.reduce()
{
  /**
	 * JS, 배열을 변형시키거나, 재정렬해주는 메서드
	 * - Array.prototype.map()	💡
	 * - Array.prototype.sort(fn) 💡
	 * 
	 * map
	 * - 유용성과 사용 빈도가 아주 높은 메소드 
	 * - 배열 요소 전체를 대상으로 함수를 호출, 함수 호출 결과를 배열로 반환 🔍
	 * 
	 * [기본 문법]
	 * let result = arr.map(function(item, index, array) {
  		// 요소 대신 새로운 값을 반환합니다.
			});
	 *
	 */
  let arr1 = [1, 2, 3, 4, 5];
  let result = arr1.map((el) => el * 2);
  console.log(result); // [ 2, 4, 6, 8, 10 ]
  console.clear();

  /**
   * Array.prototype.sort(fn) 💡
   * - 배열의 요소를 정렬해준다.
   * - 배열 자체가 변경된다. 🔍
   * - 기본 정렬 기준은 "유니코드" 순서에 따라 정렬된다. 🔍
   * - 새로운 정렬 기준을 만들려면, 기준 function 을 넘겨줘야한다. 🔍
	 * 
   * function compare(a, b) {
			if (a > b) return 1; // 첫 번째 값이 두 번째 값보다 큰 경우
			if (a == b) return 0; // 두 값이 같은 경우
			if (a < b) return -1; //  첫 번째 값이 두 번째 값보다 작은 경우
		}
	 *
	 * Array 에는 숫자, 문자열, 객체 등 정해진 것이 아닌, 알 수 없는 "무언가"로 구성된 집합이 된다.
	 * 이 "비 동질적인" 집합을 정렬해야 한다고 가정해보자.
	 * 무언가를 정렬하려면 "기준"이 필요하다. 이때 정렬 기준을 정의해주는 함수(ordering function, 정렬 함수)가 필요
	 * - sort() 메소드는 최적화된 "Quick Sort(퀵 소트)"를 사용한다. 🔍
	 * - sort() 는 주어진 함수를 사용해, 정렬 기준을 만들고 이 기준에 따라 요소들을 재배열하므로, 개발자는 내부 정렬 동작원리를 알 필요가 없다.
	 * - function 을 만들고, 이를 인수로 넘겨주는 것만 해주면 된다.
   */

  let arr2 = [7, 3, 1];
  arr2.sort(function (a, b) {
    return a - b;
  });
  console.log(arr2); // [ 1, 3, 7 ]

  let arr3 = [5, 4, 3, 2, 1];
  arr3.sort((a, b) => a - b);
  console.log(arr3); // [ 1, 2, 3, 4, 5 ]

  let arr4 = ["Österreich", "Andorra", "Vietnam"];
  console.log(arr4.sort((a, b) => (a > b ? 1 : 1))); // [ 'Österreich', 'Andorra', 'Vietnam' ] << 틀림
  console.log(arr4.sort((a, b) => a.localeCompare(b))); // [ 'Andorra', 'Österreich', 'Vietnam' ] << 맞음
  console.clear();

  /**
   * 배열의 요소를 역순으로 정렬시켜주는 메소드
   * - Array.prototype.reverse() 💡
   *
   * 반환 값은 "재정렬된 배열"
   */
  let arr5 = [1, 2, 5, 6, 1];
  arr5.reverse();
  console.log(arr5); // [ 1, 6, 5, 2, 1 ]
  console.clear();

  /**
   * - Array.prototype.split(delim) : 문자열을 분리 => 배열로 💡
   * - Array.prototype.join(gule) : 배열을 합쳐서 => 문자열로 💡
   */

  let names = "May, Jun, July";
  let arr6 = names.split(", ");
  console.log(arr6); // [ 'May', 'Jun', 'July' ]

  let test = "test";
  console.log(test.split("")); // [ 't', 'e', 's', 't' ]
  console.log(["t", "e", "s", "t"].join("")); // test
  console.clear();

  /**
	 * 배열을 기반으로 "값 하나를 도출할 때 사용"
	 * let value = arr.reduce(function(accumulator, item, index, array) {
  		// ...
			}, [initial]); 💡
	 * 
	 * accumulator : 이전 함수 호추르이 결과, initial은 함수 최초 호출시 사용되는 초깃값을 나타냄(옵션)
	 * item : 현재 배열 요소
	 * 
	 * - 이전 함수 호출 결과 -> 다음 함수를 호출 시, 첫 번째(accumulator) 인수(= previousValue) 로 사용
	 * - 마지막 함수까지 호출되면 이 값은 reduce 의 반환 값이 된다.
	 * - 맨 마지막 인자(초기값)는 생략해도 된다.
	 * - 단, 생략하는데, 배열이 비어있다 => 에러발생 🔍
	 * - 그렇기 때문에, 초기값은 그냥, "명시" 하는 것이 좋다.
	 */

  let arr7 = [1, 2, 3, 4, 5];
  let result7 = arr7.reduce((sum, current) => sum + current, 0);
  console.log(result7); // 15

  // let arr8 = [];
  // // TypeError: Reduce of empty array with no initial value
  // // 초깃값을 설정해 주었다면 초깃값이 반환되었을 겁니다.
  // arr8.reduce((sum, current) => sum + current);
}

📍 Array 집합

  • Array.prototype.every()
  • Array.prototype.some()
{
  let arr1 = [-1, -2, -3, -4, 100];
  /**
   * Array.prototype.every(fn)
   * - 배열에 모든 요소가 조건에 부합하면 true, 아니면 false
   */
  console.log(arr1.every((el) => el > 0)); // false

  /**
   * Array.prototype.some(fn);
   * - 배열에 요소들 중, 하나라도 조건에 부합하면 true, 아니면 false
   */
  console.log(arr1.some((el) => el > 0)); // true
}

📍 Array 확인, thisArg

  • Array.isArray()
{
  /**
   * Array 를 typeof 로 확인하면, 그냥 object 타입에 속한다.
   * 그렇기 때문에, 제대로, 배열인지 확인하려면
   * - Array.isArray(arr) 가 필요 💡
   */

  console.log(typeof {}); // object
  console.log(typeof []); // object

  console.log(Array.isArray({})); // false
  console.log(Array.isArray([])); // true

  /**
   * 함수를 호출하는 대부분의 배열 메소드(find, filter, map 등)는 thisArg 라는 매개변수를 옵션으로 받을 수 있다.
   *
   *
   * arr.find(func, thisArg);
   * arr.filter(func, thisArg);
   * arr.map(func, thisArg);
   * // thisArg는 선택적으로 사용할 수 있는 마지막 인수입니다.
   *
   * thisArg : function 의 this 가 된다.
   */
  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를 찾음
  // army 라는 thisArg 를 넘기지 않고, army.canJoin 사용시, 에러 발생
  let soldiers = users.filter(army.canJoin, army);

  console.log(soldiers.length); // 2
  console.log(soldiers[0].age); // 20
  console.log(soldiers[1].age); // 23
}

결론

다음은, Array Object 메소드들 중에 중복확인을 위해 includes 같은 일부 메소드들을 사용해 어떤 하나의 작업을 하는데 O(N) 시간이 걸리고, 이 작업이 특정 반복문에 속해있고, N 이 커지면, 성능상 좋지 못하다.
이러한 중복확인 작업을 효율적으로 할 수 있는 Map 이나 Set 자료형에 대해 알아본다.


참고

profile
🌟 Let the brighter shine the brighter.

0개의 댓글