Equal Sides Of An Array<6 kyu>

jjanmo·2020년 1월 25일
0

Codewars에서 뒹굴기

목록 보기
24/32

문제링크

문제

1️⃣
You are going to be given an array of integers. Your job is to take that array and find an index N where the sum of the integers to the left of N is equal to the sum of the integers to the right of N. If there is no index that would make this happen, return -1.

For example:
Let's say you are given the array {1,2,3,4,3,2,1}:
Your function will return the index 3, because at the 3rd position of the array, the sum of left side of the index ({1,2,3}) and the sum of the right side of the index ({3,2,1}) both equal 6.

Let's look at another one.
You are given the array {1,100,50,-51,1,1}:
Your function will return the index 1, because at the 1st position of the array, the sum of left side of the index ({1}) and the sum of the right side of the index ({50,-51,1,1}) both equal 1.

2️⃣
Last one:
You are given the array {20,10,-80,10,10,15,35}
At index 0 the left side is {}
The right side is {10,-80,10,10,15,35}
They both are equal to 0 when added. (Empty arrays are equal to 0 in this problem)
Index 0 is the place where the left side and right side are equal.

Note: Please remember that in most programming/scripting languages the index of an array starts at 0.

3️⃣
Input:
An integer array of length 0 < arr < 1000. The numbers in the array can be any integer positive or negative.

Output:
The lowest index N where the side to the left of N is equal to the side to the right of N. If you do not find an index that fits these rules, then you will return -1.

Note:
If you are given an array with multiple answers, return the lowest correct index.

🚩문제간단설명
1️⃣은 문제에서 기본적으로 요구하는 사항에 대한 설명이다. 이를 도식화하면 이렇다.

2️⃣은 만약에 결과값인 index가 0이 되는 상황에 대한 설명이다. index가 0을 기준으로 했을 때, 왼쪽은 아무값도 없기 때문에 왼쪽의 총합은 0이 된다. 오른쪽은 index가 1부터 배열 끝까지의 합을 구하면 되는데, 이 때 합이 0이 되면 최종적으로 결과값 index가 0이 될 수 있는 상황을 예시로 보여주고 있다.

3️⃣은 input과 output에 대한 추가적인 조건들에 대한 설명이다.

문제 접근

  • 제출 코드
  function findEvenIndex(arr) {
    let equalIdx = -1;
    for(let i = 0; i < arr.length; i++){
      const leftSum = arr.slice(0,i).reduce((a,c)=>a+c,0);
      const rightSum = arr.slice(i+1).reduce((a,c)=>a+c,0);
      if(leftSum === rightSum) {
        equalIdx = i;
        break;
      }
    }
    return equalIdx;
  }

for문에서 i는 기준점을 말한다. 그 기준점을 중심으로 왼쪽과 오른쪽의 합을 구하여서 비교한다.

참고로 부분배열을 알기 위해서 slice()를 사용하였다. 부분배열을 만들 수 있는, 배열을 자르고 복사하는 여러가지 메소드들이 있는데, 그 중에서 slice()는 **얕은 복사로서 원배열을 수정시키지 않는다**. 만약에 위에서 slice()대신 splice()(`splice() : 깊은 복사로서 원배열을 변형시킨다.`)를 사용했다면 원배열이 변형되면서 원하는 결과값이 나오지 않았을 것이다.  
  • forEach()를 사용한 코드

    ```javascript

    1 function findEvenIndex(arr) {
    2 let equalIdx = -1;
    3 arr.forEach((v,i) => {
    4 const leftSum = arr.slice(0,i).reduce((a,c)=>a+c,0);
    5 const rightSum = arr.slice(i+1).reduce((a,c)=>a+c,0);
    6 if(leftSum === rightSum) {
    7 equalIdx = i;
    8 break; //error
    9 }
    10 });
    11 return equalIdx;
    12 }

위의 코드는 오류가 난다. 처음엔 '왜 오류지?' 라는 생각을 했다. 바로 8번라인의 break 키워드 때문이다. break를 사용한 이유는 arr 배열을 모두 순회하고 싶지 않기 때문이다. 문제 output 조건에서도 나와있듯이`  the lowest index` 하나의 값만 구하면 되기 때문에, 조건에 맞는 값이 하나가 나오면 더 이상 반복할 필요가 없다. 그런데 문제는 forEach()안에서는 break 키워드를 사용할 수 없다는 것이다. break문이 없는 이유에 대해서 찾아봤는데, 미처 구현을 안한 것(?)이라고 한다 😅 뭐 그럴 수 있지...  이를 해결하기 위해선 **`Break`라는 커스텀 오류객체를 정의해서 순회하다가 멈춰야 하는 곳에서 예외를 던져서 순회를 멈출 수 있다** 라고 한다.(아웃사이더님의 말 인용👍) 여기에 try-catch문을 이용하였다. 코드는 아래와 같다. 
```javascript
  function findEvenIndex(arr) {
      var Break = new Error('Break'); //오류객체 생성
      let equalIdx = -1;
      try{
        arr.forEach((v,i)=>{
          const leftSum = arr.slice(0,i).reduce((a,c)=>a+c,0);
          const rightSum = arr.slice(i+1).reduce((a,c)=>a+c,0);
          if(leftSum === rightSum) {
            equalIdx = i;
            throw Break;	//순회를 멈추고 싶은 곳에 오류를 인위적으로 throw
          }
        });
      }
      catch(e){
         if (e!= Break) throw Break; 
      }
      return equalIdx;
  }

뭔가 복잡하다. '이렇게까지 써야할까' 라는 생각이 저절로 드는 코드이다.

가독성도 떨어지고 코드의 복잡도도 높아지기 때문에 이를 해결할 수 있는 또 다른 방법으로는 some() 메소드를 이용하는 것이다. 
  • some()을 이용한 풀이
    - some() 메소드에 대해서
    arr.some(callback) : callback이 true를 반환하면 그 순간까지만 순회를 하고 결과값으로 true를 리턴하고 메소드를 종료한다. 즉, callback이 참(불린으로 변환했을 때 true가 되는 값)을 반환하는 요소를 찾을 때까지 배열에 있는 각 요소에 대해 한 번씩 callback 함수를 실행한다.

    - some()을 이용해 수정한 코드
    ```javascript
        function findEvenIndex(arr) {
          let equalIdx = -1;
          arr.some((v,i) => {
            const leftSum = arr.slice(0,i).reduce((a,c)=>a+c,0);
            const rightSum = arr.slice(i+1).reduce((a,c)=>a+c,0);
            if(leftSum === rightSum) {
              equalIdx = i;
            }
            return leftSum === rightSum;
          });
          return equalIdx;
        }
forEach()를 이용하기 위해서 try-catch를 이용한 코드보다는 더 깔끔하고 가독성도 높아졌다. 하지만 기본적으로 some()은 **배열 안에 특정요소의 존재 여부를 판별하기 위한 목적으로 만들어진 메소드**이다. 그런데 여기선 단순하게 중간에 순회를 멈추기 위한 수단으로서 이를 사용하였다. 이것은 추천하지 않을 some()의 용례가 아닐까 생각한다. 

## Best Solution
```javascript
  function findEvenIndex(arr) {
    for(var i=1; i<arr.length-1; i++) {
      if(arr.slice(0, i).reduce((a, b) =>  a+b) === arr.slice(i+1).reduce((a, b) =>  a+b)) {
        return i;
      }
    }
    return -1;
  }

내 풀이를 변수를 사용하지 않고 압축해 놓은 버전인 것 같다. 내 풀이와 이 풀이 중 무엇이 더 좋은 코드일까? 어느 것이 더 읽기 좋은 코드일까? 개인적으로 Best Solution이 좋은 것 같다. 그런데 변수를 할당해서 사용하는 것이 좋은 코딩 습관이라는 것을 들은 적이 있다. 여기선 변수를 사용하는 것이나 사용하지 않은 것이나 별로 차이가 없지만, 수천줄짜리 코드의 일부분이라면? 어느 것이 좋은 코드인지에 대해서는 계속 생각해 볼 문제인 것 같다.

결론

한 문제를 여러가지 메소드들을 사용하여 수정하면서 메소드의 작동원리에 대해서 좀 더 세밀하게 알게 되었다. 또한 메소드의 존재 이유에 대해서 생각 할 수 있는 시간이였다.

참고

forEach에 break문 대신 some 사용하기
MDN Array.prototype.some()

profile
눈길을 걸어갈 때 어지럽게 걷지 말기를.

0개의 댓글