본문 바로가기
프로그래밍/JavaScript

[JS] reduce index와 함께 효과적으로 쓰기 / 최솟값 만들기 풀이 예시 / ?? 논리 연산자

by hi-rachel 2023. 1. 22.

 

[프로그래머스] 최솟값 만들기 문제 예시 

길이가 같은 배열 A, B 두개가 있습니다. 각 배열은 자연수로 이루어져 있습니다.
배열 A, B에서 각각 한 개의 숫자를 뽑아 두 수를 곱합니다. 이러한 과정을 배열의 길이만큼 반복하며, 두 수를 곱한 값을 누적하여 더합니다. 이때 최종적으로 누적된 값이 최소가 되도록 만드는 것이 목표입니다. (단, 각 배열에서 k번째 숫자를 뽑았다면 다음에 k번째 숫자는 다시 뽑을 수 없습니다.)

예를 들어 A = [1, 4, 2] , B = [5, 4, 4] 라면

  • A에서 첫번째 숫자인 1, B에서 첫번째 숫자인 5를 뽑아 곱하여 더합니다. (누적된 값 : 0 + 5(1x5) = 5)
  • A에서 두번째 숫자인 4, B에서 세번째 숫자인 4를 뽑아 곱하여 더합니다. (누적된 값 : 5 + 16(4x4) = 21)
  • A에서 세번째 숫자인 2, B에서 두번째 숫자인 4를 뽑아 곱하여 더합니다. (누적된 값 : 21 + 8(2x4) = 29)

즉, 이 경우가 최소가 되므로 29를 return 합니다.

배열 A, B가 주어질 때 최종적으로 누적된 최솟값을 return 하는 solution 함수를 완성해 주세요.

제한사항

  • 배열 A, B의 크기 : 1,000 이하의 자연수
  • 배열 A, B의 원소의 크기 : 1,000 이하의 자연수

입출력 예

A B answer
[1, 4, 2] [5, 4, 4] 29
[1,2] [3,4] 10

입출력 예 설명

 

입출력 예 #1
문제의 예시와 같습니다.

 

입출력 예 #2
A에서 첫번째 숫자인 1, B에서 두번째 숫자인 4를 뽑아 곱하여 더합니다. (누적된 값 : 4) 다음, A에서 두번째 숫자인 2, B에서 첫번째 숫자인 3을 뽑아 곱하여 더합니다. (누적된 값 : 4 + 6 = 10)
이 경우가 최소이므로 10을 return 합니다.

 

 


 

function solution(A,B){
    let answer = [];
    let sortA = A.sort((a,b) => a-b);
    let sortB = B.sort((a,b) => b-a);
    for (let i = 0; i < A.length; i++) {
        answer.push(sortA[i] * sortB[i]);
    }
    return answer.reduce((acc, cur) => acc + cur, 0);
}

 

 

내가 위 문제를 푼 방식이다.

 

- 다른 풀이

function solution(A,B){
    A.sort((a, b) => a - b)
    B.sort((a, b) => b - a)
    return A.reduce((total, val, idx) => total + val * B[idx], 0)
}

하지만 풀고 나서 다른 풀이를 참고해 보니 굳이 for문으로 index를 지정해 줄 필요가 없다!

0 초기값부터 시작해 A의 값과 B의 값을 index 순으로 곱하고 곱한 값을 하나씩 다 더해준다.

 

물론 reduce도 반복적인 방법으로 만들어진 메서드로 for문과 시간복잡도는 차이가 없겠지만 reduce 메서드를 사용하면 더 간단하게 표현하고 가독성을 높일 수도 있다.

 

▶ Array.prototype.reduce()

[참고] 2023.01.06 - [코테 문풀] - [프로그래머스] 자릿수 더하기 - JavaScript / reduce()에 대해 이해해보자!

 

[프로그래머스] 자릿수 더하기 - JavaScript / reduce()에 대해 이해해보자!

✏️ 문제 설명 정수 n이 매개변수로 주어질 때 n의 각 자리 숫자의 합을 return하도록 solution 함수를 완성해주세요 제한사항 0 ≤ n ≤ 1,000,000 입출력 예 n result 1234 10 930211 16 입출력 예 설명 입출력

hi-rachel.tistory.com

이전 글에서도 reduce에 정리를 해놨었는데 currentIndex 매개변수를 사용하는 건 익숙하지 않았기 때문에 다시 정리하고 넘어가려고 한다.

 

const array = [15, 16, 17, 18, 19];

function reducer(accumulator, currentValue, index) {
  const returns = accumulator + currentValue;
  console.log(
    `accumulator: ${accumulator}, currentValue: ${currentValue}, index: ${index}, returns: ${returns}`,
  );
  return returns;
}

array.reduce(reducer);

console

initialValue가 없이 사용할 때 예시 코드. index는 1부터 시작한다. (initialValue가 지정되면 0부터 시작한다)

 

매개변수

callbackFn

: 배열의 각 요소에 대해 실행할 함수. 반환 값은 accumulator이고 다음에 callbackFn를 호출할 때 매개변수의 값이 된다. 마지막 호출의 경우 반환 값은 reduce()의 반환 값이 됩니다.

 

accumulator은 callbackFn에 대한 이전 호출의 결과 값. 첫 번째 호출에서 initialValue가 지정된 경우 initialValue, 지정되지 않는다면 array[0]의 값.

 

currentValue는 현재 요소의 값. 첫 번째 호출에서 initialValue가 지정된 경우 array[0]의 값, 지정되지 않는다면 array[1]의 값(accumulator의 값이 array[0]이 되기 때문)

 

currentIndex는 currentValue 배열에서 인덱스 위치. 첫 번째 호출에서 initialValue가 지정되면 0, 아니면 1(initialValue가 0이므로).

 

array : array reduce()가 호출됨.

 

initialValue(선택)는 콜백이 처음 호출될 때 accumulator가 초기화되는 값. initialValue가 지정되면 callbackFn은 배열 첫 번째 값을 currentValue로 사용해 실행을 시작. initialValue가 지정되지 않은 경우 initialValue는 accumulator 배열의 첫 번째 값으로 초기화되고 배열의 두 번째 값을 currentValue을 지정해 실행을 시작. 이 경우 배열이 비어 있으면(accumulator로 반환할 첫 번째 값이 없음) 오류가 발생.(TypeError)

 

예시

객체 배열의 값 합계(Sum of values in an object array)

const objects = [{ x: 1 }, { x: 2 }, { x: 3 }];
const sum = objects.reduce(
  (accumulator, currentValue) => accumulator + currentValue.x,
  0,
);

console.log(sum); // 6

위와 같이 객체 array의 값의 합계도 구할 수 있다. currentValue.x로 key를 명시해 주면 된다.

 

배열의 배열 병합(Flatten an array of arrays)

const flattened = [
  [0, 1],
  [2, 3],
  [4, 5],
].reduce((accumulator, currentValue) => accumulator.concat(currentValue), []);
// flattened is [0, 1, 2, 3, 4, 5]

초기값으로 []을 주고, 거기에 concat을 해가면 array를 병합할 수 있다.

 

객체에서 값의 인스턴스 계산(Counting instances of values in an object)

const names = ["Alice", "Bob", "Tiff", "Bruce", "Alice"];

const countedNames = names.reduce((allNames, name) => {
  const currCount = allNames[name] ?? 0;
  return {
    ...allNames,
    [name]: currCount + 1,
  };
}, {});
// countedNames is:
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }

names의 배열 안에 각 name을 바로 count 해주는 방식

 

+ ▶ Nullish coalescing operator (??)

leftExpr ?? rightExpr

Nullish 병합 ?? 연산자는 왼쪽 피연산자가 null 또는 undefined일 때 오른쪽 피연산자를 반환하고 그렇지 않으면 왼쪽 피연산자를 반환하는 논리 연산자이다.

 

const foo = null ?? 'default string';
console.log(foo);
// Expected output: "default string"

const baz = 0 ?? 42;
console.log(baz);
// Expected output: 0

 

속성별로 객체 그룹화(Grouping objects by a property)

const people = [
  { name: "Alice", age: 21 },
  { name: "Max", age: 20 },
  { name: "Jane", age: 20 },
];

function groupBy(objectArray, property) {
  return objectArray.reduce((acc, obj) => {
    const key = obj[property];
    const curGroup = acc[key] ?? [];

    return { ...acc, [key]: [...curGroup, obj] };
  }, {});
}

const groupedPeople = groupBy(people, "age");
console.log(groupedPeople);
// {
//   20: [
//     { name: 'Max', age: 20 },
//     { name: 'Jane', age: 20 }
//   ],
//   21: [{ name: 'Alice', age: 21 }]
// }

각 obj를 지정해 주는 property대로(key) 분류해 해당 key를 가진 obj를 array로 묶어 반환하는 groupBy 함수를 만들고, "age"를 property로 지정해 주면 나이별로 people을 group 할 수 있다.

 

확산 구문(...) 및 initialValue를 사용하여 객체 배열에 포함된 배열 연결(Concatenating arrays contained in an array of objects using the spread syntax and initialValue)

// friends - an array of objects
// where object field "books" is a list of favorite books
const friends = [
  {
    name: "Anna",
    books: ["Bible", "Harry Potter"],
    age: 21,
  },
  {
    name: "Bob",
    books: ["War and peace", "Romeo and Juliet"],
    age: 26,
  },
  {
    name: "Alice",
    books: ["The Lord of the Rings", "The Shining"],
    age: 18,
  },
];

// allbooks - list which will contain all friends' books +
// additional list contained in initialValue
const allbooks = friends.reduce(
  (accumulator, currentValue) => [...accumulator, ...currentValue.books],
  ["Alphabet"],
);
console.log(allbooks);
// [
//   'Alphabet', 'Bible', 'Harry Potter', 'War and peace',
//   'Romeo and Juliet', 'The Lord of the Rings',
//   'The Shining'
// ]

초기값을 ["Alphabet"]로 줘서 같이 넣어주고 ...currentValue.books로 obj에서 books를 지정해 books만 새로운 배열에 넣어줬다.

참고로 accumulator에 concat 같은 역할을 하는 ...을 적어주지 않으면 배열 안에 계속 배열이 추가되는 형태로 들어가 버린다.

currentValue.book에 ...을 적어주지 않으면 ['Alphabet', Array(2), Array(2), Array(2)] 형태로 Array 그대로 들어가 버린다.

 

[참고] 2023.01.12 - [코테 문풀] - [프로그래머스] 등수 매기기 - JavaScript / ES6 ... 연산자란? 사용 예시

 

[프로그래머스] 등수 매기기 - JavaScript / ES6 ... 연산자란? 사용 예시

✏️ 문제 설명 영어 점수와 수학 점수의 평균 점수를 기준으로 학생들의 등수를 매기려고 합니다. 영어 점수와 수학 점수를 담은 2차원 정수 배열 score가 주어질 때, 영어 점수와 수학 점수의 평

hi-rachel.tistory.com

 

배열에서 중복 항목 제거(Remove duplicate items in an array)

const myArray = ["a", "b", "a", "b", "c", "e", "e", "c", "d", "d", "d", "d"];
const myArrayWithNoDuplicates = myArray.reduce((accumulator, currentValue) => {
  if (!accumulator.includes(currentValue)) {
    return [...accumulator, currentValue];
  }
  return accumulator;
}, []);

console.log(myArrayWithNoDuplicates);    // ['a', 'b', 'c', 'e', 'd']

※ 참고로 같은 효과인 const arrayWithNoDuplicates = Array.from(new Set(myArray)) 이런 식으로 Array.from, Set을 사용하는 것이 더 좋은 성능을 낸다.

 

 

 

모든 코드 예시는 mdn 문서에서 가져왔습니다. 메서드의 다양한 활용 방식을 정리해 두면 계속 도움이 된다!

 

🙂 공부하면서 정리한 글입니다. 공감과 피드백 환영합니다.