ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 10/25 TIL | 코테 여러 번 풀기? 에러 발생? 오히려 좋아!
    📝 기록/매일의 기록 2022. 10. 25. 18:09
     

    어제와 오늘의 코딩 도장 문제 풀이! 프로그래머스의 K번째 수라는 문제였고, 어제는 Java, 오늘은 JavaScript로 문제를 풀었다!

    문제 설명

    배열 array의 i번째 숫자부터 j번째 숫자까지 자르고 정렬했을 때, k번째에 있는 수를 구하려 합니다.
    예를 들어 array가 [1, 5, 2, 6, 3, 7, 4], i = 2, j = 5, k = 3이라면

    - array의 2번째부터 5번째까지 자르면 [5, 2, 6, 3]입니다.
    - 1에서 나온 배열을 정렬하면 [2, 3, 5, 6]입니다.
    - 2에서 나온 배열의 3번째 숫자는 5입니다.

    배열 array, [i, j, k]를 원소로 가진 2차원 배열 commands가 매개변수로 주어질 때, commands의 모든 원소에 대해 앞서 설명한 연산을 적용했을 때 나온 결과를 배열에 담아 return 하도록 solution 함수를 작성해주세요.

    제한 사항

    - array의 길이는 1 이상 100 이하입니다.
    - array의 각 원소는 1 이상 100 이하입니다.
    - commands의 길이는 1 이상 50 이하입니다.
    - commands의 각 원소는 길이가 3입니다.

    입출력 예

    array answer return
    [1, 5, 2, 6, 3, 7, 4] [[2, 5, 3], [4, 4, 1], [1, 7, 3]]
    [5, 6, 3]

    근데 홀맨님이 API와 알고리즘 직접 구현, 두 가지 방식으로 풀라고 말씀하셔서 한번 그렇게도 풀어보았다!

    그렇게 탄생한 총 3개의 PR..!

    나의 문제 풀이

    [📃 SolutionTest.java - 테스트 코드]

    import org.junit.jupiter.api.Test;
    
    import static org.junit.jupiter.api.Assertions.*;
    
    class SolutionTest {
    
        @Test
        void sliceAndSort() {
            Solution solution = new Solution();
    
            int[] array = {1, 5, 2, 6, 3, 7, 4};
            int[][] command = {{2, 5, 3},{4, 4, 1},{1, 7, 3}};
    
            assertEquals(5, solution.sliceAndSort(array, command[0]));
            assertEquals(6, solution.sliceAndSort(array, command[1]));
            assertEquals(3, solution.sliceAndSort(array, command[2]));
        }
    
    
        @Test
        void command() {
            Solution solution = new Solution();
    
            int[] array = {1, 5, 2, 6, 3, 7, 4};
            int[][] commands = {{2, 5, 3},{4, 4, 1},{1, 7, 3}};
    
            assertArrayEquals(new int[]{5, 6, 3}, solution.getResult(array, commands));
        }
    }

    [📃 Solution.java - 문제 풀이 코드]

    import java.util.Arrays;
    
    public class Solution {
        public int[] solution(int[] array, int[][] commands) {
            return getResult(array, commands);
        }
    
        // 특정 인덱스로 slice하고 정렬하는 함수
        public int sliceAndSort(int[] array, int[] command) {
            return Arrays.stream(array, command[0] - 1, command[1])
                    .sorted()
                    .toArray()[command[2] - 1];
        }
    
        // 결과를 반환하는 코드
        public int[] getResult(int[] array, int[][] commands) {
            return Arrays.stream(commands)
                    .mapToInt(command -> sliceAndSort(array, command))
                    .toArray();
        }
    }

    파라미터로 입력받은 commands가 우선 2차원 배열이기 때문에 Arrays.stream() 메서드로 map을 한번 돌면서 각 command 배열을 sliceAndSort()의 두 번째 인자로 넣어준다.

    sliceAndSort() 메서드는 파라미터로 들어온 array와 command 배열을 받은 뒤, Arrays.stream()으로 돈다. 이때 첫 번째 인자는 array, 두 번째, 세 번째 인자는 시작 인덱스와 끝 인덱스를 넣어준다. 그러고 정렬하고 array로 만든다. 그러고 command의 3번째 인자인 결과로 받고 싶은 인덱스의 숫자를 반환하게 작성하였다.

    그렇게 sliceAndSort()가 돌려준 결과 값을 array로 만들어 반환하면 문제 풀이 끝!

    import java.util.Arrays;
    
    public class Solution {
        public int[] solution(int[] array, int[][] commands) {
            return getResult(array, commands);
        }
    
        // with Api
        public int withApi(int[] array, int[] command) {
            return Arrays.stream(Arrays.copyOfRange(array, command[0] - 1, command[1]))
                    .sorted()
                    .toArray()[command[2] - 1];
        }
    
        public int[] getResult(int[] array, int[][] commands) {
            return Arrays.stream(commands)
                    .mapToInt(command -> withApi(array, command))
                    .toArray();
        }
    }

    copyOfRange 메서드를 넣어서 풀어보긴 했는데 내가 이미 그렇게 작성을 해서 그런지 오히려 Arrays.copyOfRange()가 하나 더 추가됐을 뿐 달라진 게 없었다..! 아마 IntStream을 쓴 경우 효과가 극대화되는 거 같다..!?

    [📃 solution.test.js - 테스트 코드]

    import solution, { sliceAndSort } from './solution';
    
    const array = [1, 5, 2, 6, 3, 7, 4];
    const commands = [
      [2, 5, 3],
      [4, 4, 1],
      [1, 7, 3],
    ];
    
    test('배열을 짜르고 정렬하자', () => {
      expect(sliceAndSort(array, commands[0])).toBe(5);
      expect(sliceAndSort(array, commands[1])).toBe(6);
      expect(sliceAndSort(array, commands[2])).toBe(3);
    });
    
    test('sliceAndSort 연산 결과를 배열에 담자', () => {
      expect(solution(array, commands)).toStrictEqual([5, 6, 3]);
    });

    [📃 solution.js - 문제 풀이 코드]

    export function sliceAndSort(array, command) {
      return array.slice(command[0] - 1, command[1])
      			.sort((a, b) => a - b)[command[2] - 1];
    }
    
    export default function solution(array, commands) {
      return commands.map((i) => sliceAndSort(array, i));
    }

    JavaScript 코드도 Java와 달라질 건 없었다..! 사용하는 메서드는 좀씩 다르지만 내용은 똑같다!

    동기들의 문제 풀이

    ✅ Java

    쥬쥬's 문제 풀이 : IntStream

    import java.util.*;
    import java.util.stream.*;
    
    class Solution {
        public int[] solution(int[] array, int[][] commands) {
            int[] answer = getAllNumbers(array, commands);
            return answer;
        }
    
        public int getTheNumberOnce(int[] array, int[] command) {
            return IntStream.range(command[0] - 1, command[1])
                    .map(i -> array[i])
                    .sorted()
                    .toArray()[command[2] - 1];
        }
    
        public int[] getAllNumbers(int[] array, int[][] commands) {
            return IntStream.range(0, commands.length)
                    .map(i -> getTheNumberOnce(array, commands[i]))
                    .toArray();
        }
    }

    제나's 문제 풀이 : for-loop

    import java.util.Arrays;
    
    class Solution {
        public int[] solution(int[] array, int[][] commands) {
            int[] answer = new int[commands.length];
    
            for (int i = 0; i < answer.length; i++) {
                int[] temp = Arrays.copyOfRange(array, commands[i][0] - 1, commands[i][1]);
    
                Arrays.sort(temp);
    
                answer[i] = temp[commands[i][2] - 1];
            }
    
            return answer;
        }
    }

    ✅ JavaScript

    홀맨's 문제 풀이 : map 1회 

    function solution(array, commands) {
        return kNumber(array, commands);
    }
    
    const kNumber = (array, commands) => 
      commands.map(([start, end, k]) => 
        array
          .slice(start - 1, end)
          .sort(compare)[k - 1]
      );
    
    const compare = (a, b) => a - b;

    홀맨님의 코드가 확실히 깔끔하고 나처럼 메서드를 두 번 쪼개지 않고 한번 map 돌 때 slice, sort를 같이 하니까 이게 더 좋은 거 같다! 그리고 내 코드의 경우 함수 네이밍이 좀 아쉬운 거 같기도 하다. 풀 때 그런 사소한 부분까지 신경 써보자! 여러 번 다른 코드로 풀어보고 읽어보니 나중에는 그럼 이렇게 짜 봐야겠다 하는 인사이트를 얻을 수 있었다 👍🏻


    오늘 강의 반복 과제를 하다가 어제처럼 영속성 기능을 구현하면서 새로운 문제를 만났는데 오늘도 TIL로 한번 정리해보려고 한다.

    영속화를 하면서 postRepository가 interface로 변경되었고, mock 객체를 사용하는 방식으로 create 테스트를 수정하였는데, 43번째 줄 id가 not null인지 확인하는 테스트가 에러를 뱉어내는 것이다! not null이어야 하는데 null이라는....

    에러를 뱉어내는 위치를 찾아가 확인해보니 위와 같이 작성되어 있었는데, of() 메서드로 postDto를 Post로 변환해주고, 그 포스트를 postRepository에 save 해줬는데 정작 return할 때는 post.toDto() 즉, 기존 post를 dto로 변환해 보내주고 있었던 것이다.

    interface로 변경되기 전 코드

    postRepository에서는 이렇게 save할 때 id 값을 생성해주고 있기 때문에 기존에 반환했던 postRepository를 거치지 않은 post.toDto는 당연히 id가 null이다.

    postRepository가 interface로 변경되면서 위와 같이 findAll, save 메서드만 남았는데 구현이 사라지니까 가시적이지 못하니까 더 혼란스러운 거 같다...

    그래서 postRepository에 저장한 post를 dto로 변환해서 return 하였더니 해결이 되었다! 👍🏻 어제부터 영속성 작업을 할 때마다 새로운 에러들이 등장하는데 ㅋㅋㅋㅋㅋ 나만 계속 이런 에러를 마주해서 동기들이랑 머리를 맞대서 해결했다.... 아직 내가 강의를 완벽하게 이해하지 못했다는 증거겠지.. 다만 이런 에러가 나타나면 나타날수록 오히려 이해도는 더 높아지는 거 같다! 오히려 좋아~