티스토리 뷰

[문제 링크]

https://school.programmers.co.kr/learn/courses/30/lessons/42579

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

public class Quiz05 {
    public static void main(String[] args) {
        String[] genres = {"classic", "pop", "rock", "classic", "rock", "metal", "jazz"};
        int[] plays = {1000, 600, 300, 1000, 500, 1000, 700};

        hashing(genres, plays);
    }

    public static int[] hashing(String[] genres, int[] plays) {

        LinkedHashMap<Integer, String> hashMap = new LinkedHashMap<>();

        for (int i = 0; i < genres.length; i++) {
            hashMap.put(plays[i], genres[i]);
        }

       // 장르별 총 재생횟수 계산
        HashMap<String, Integer> totalPlays = new HashMap<>();

        for (int i = 0; i < genres.length; i++) {
            String genre = genres[i];
            int play = plays[i];

            //이미 장르가 있으면 현재 플레이 횟수에 지금 플레이 횟수를 더함
            //아니면 그냥 집어넣음
            if (totalPlays.containsKey(genre)) {
                totalPlays.put(genre, totalPlays.get(genre) + play);
            } else {
                totalPlays.put(genre, play);
            }

        }

        //베스트 앨범에 수록할 노래 선택
        //해시맵은 순서를 보장하지 않으니 먼저 정렬해줘야됨
        List<String> genreSortedByPlays = totalPlays.entrySet().stream()
                .sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());


        System.out.println("genreSortedByPlays : " + genreSortedByPlays);
        //많이 플레이된 장르 순으로 정렬

        List<Integer> answerList = new ArrayList<>();
        for (String genreSortedByPlay : genreSortedByPlays) {

            List<Integer> songsInGenre = new ArrayList<>();

            for (int i = 0; i < genres.length; i++) {
                if (genreSortedByPlay.equals(genres[i])) {
                    songsInGenre.add(i);
                }
            }

            System.out.println("songInGenre : " + songsInGenre);

            Comparator<Integer> comparator = new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    // o1과 o2를 비교하는 비교 로직 작성
                    // o1이 작을 경우 음수, 같을 경우 0, 큰 경우 양수를 리턴하도록 구현
                    // 예: 오름차순으로 정렬할 경우 o1 - o2를 리턴

                    //재생횟수가 같지 않으면 내림차순으로 정렬
                    if (plays[o1] != plays[o2]) {
                        return plays[o2] - plays[o1];
                    }
                    //재생횟수가 같으면 인덱스가 낮은 것부터
                    return o1 -o2;
                }
            };

            Collections.sort(songsInGenre, comparator);

            //장르에 속한 곡이 1개인 경우가 있으니, 최대 고를 수 있는 갯수인 2개와 비교해서 for문 길이를 잡아줌

            int maxSongsToPick = Math.min(2, songsInGenre.size());
            for (int i = 0; i < maxSongsToPick; i++) {
                answerList.add(songsInGenre.get(i));
            }

        }




//        LinkedHashMap<Integer, String> sortedHashMap = hashMap.entrySet().stream().sorted(Map.Entry.comparingByValue())
//                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
//                        (e1, e2) -> e2, LinkedHashMap::new));
//
//
//        /*
//         * toMap 파라미터들(순서대로)
//         * @param keyMapper a mapping function to produce keys -> 키매핑
//         * @param valueMapper a mapping function to produce values -> 밸류매핑
//         * @param mergeFunction a merge function, used to resolve collisions between
//         *                      values associated with the same key, as supplied
//         *                      to {@link Map#merge(Object, Object, BiFunction)} -> key가 동일할때 두개의 요소를 어떻게 병합할지. 위에서는 새로운 값으로 덮어쓰게 했음.
//         * @param mapFactory a supplier providing a new empty {@code Map}
//         *                   into which the results will be inserted*/
//

        for (Integer integer : hashMap.keySet()) {
            System.out.println(integer + " " + hashMap.get(integer) );
        }

        System.out.println("=================================");

        for (String s : totalPlays.keySet()) {
            System.out.println(s + " " + totalPlays.get(s));
        }

        System.out.println("================================");
        System.out.println(answerList);


        int[] answer = new int[answerList.size()];

        for (int i = 0; i < answer.length; i++) {
            answer[i] = answerList.get(i);

        }

        return answer;

    }
}

해시를 써서 풀면 되겠다 이후에는 어떻게하나 싶다가 여기 저기 다 찾아보고 풀은 방식. 
comparator는 아직도 모르겠어서 이것도 찾아봐서 알았다. 컬렉션 sort는 항상 찾아보고 푸네 ㅠ

나중에 다시 풀어봐야겠다.

 

1.먼저 각 장르의 총 재생횟수를 계산한다.

 

2.베스트 앨범에 수록할 노래를 선택한다.

 

List<String> genreSortedByPlays = totalPlays.entrySet().stream()
        .sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
        .map(Map.Entry::getKey)
        .collect(Collectors.toList());

위 코드는 각 장르의 총 재생횟수 순으로 내림차순 정렬을 하는 코드이다.

totalPlays.entrySet().stream()

이 코드는 해시맵의 모든 키-값 쌍을 담은 Set<Map.Entry<K,V>>을 반환한다.

따라서 entrySet()을 사용하면 해시맵의 모든 키-값 쌍을 순회할 수 있다.

 

.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))

밸류를 기준으로 내림차순으로 정렬한다.

 

.map(Map.Entry::getKey)
//.map(totalPlay -> totalPlay.getKey())

대상이 되는 해시맵에서 객체를 해시맵의 키로 매핑한다.

그런데 이거보다 아래 코드가 좀 더 보기 편하고 이해하기 쉬운 것 같다

List<String> genreSortedByPlays = new ArrayList<>(totalPlays.keySet());
Collections.sort(genreSortedByPlays, (a, b) -> totalPlays.get(b).compareTo(totalPlays.get(a)));

totalPlays의 모든 키를 가져와서 리스트를 만들고, 내림차순으로 정렬되게 한다.

 

맨 처음 코드가 성능이 좀 더 좋다는데, 솔직히 내가 제대로 쓴 코드도 아니고 제대로 이해도 못했다.
그래도 더 낫다니 일단 써두고 필요하면 봐야겠다.

//베스트 앨범에 수록할 노래 선택
        //해시맵은 순서를 보장하지 않으니 먼저 정렬해줘야됨
        List<String> genreSortedByPlays = new ArrayList<>(totalPlays.keySet());
        Collections.sort(genreSortedByPlays, (a, b) -> totalPlays.get(b).compareTo(totalPlays.get(a)));


        System.out.println("genreSortedByPlays : " + genreSortedByPlays);
        //많이 플레이된 장르 순으로 정렬

        List<Integer> answerList = new ArrayList<>();
        for (String genreSortedByPlay : genreSortedByPlays) {

            List<Integer> songsInGenre = new ArrayList<>();

            for (int i = 0; i < genres.length; i++) {
                if (genreSortedByPlay.equals(genres[i])) {
                    songsInGenre.add(i);
                }
            }

            System.out.println("songInGenre : " + songsInGenre);

            Comparator<Integer> comparator = new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    // o1과 o2를 비교하는 비교 로직 작성
                    // o1이 작을 경우 음수, 같을 경우 0, 큰 경우 양수를 리턴하도록 구현
                    // 예: 오름차순으로 정렬할 경우 o1 - o2를 리턴

                    //재생횟수가 같지 않으면 내림차순으로 정렬
                    if (plays[o1] != plays[o2]) {
                        return plays[o2] - plays[o1];
                    }
                    //재생횟수가 같으면 인덱스가 낮은 것부터
                    return o1 -o2;
                }
            };

            Collections.sort(songsInGenre, comparator);

            //장르에 속한 곡이 1개인 경우가 있으니, 최대 고를 수 있는 갯수인 2개와 비교해서 for문 길이를 잡아줌

            int maxSongsToPick = Math.min(2, songsInGenre.size());
            for (int i = 0; i < maxSongsToPick; i++) {
                answerList.add(songsInGenre.get(i));
            }

        }

먼저 정렬된 장르를 도는 for문을 하나 만든다.

그리고 각 장르에서 최대 2곡을 뽑기 위해서 리스트를 만든다.

 

그리고 다시 for문 안에서
문제에서 주어진 genre 배열의 i번째 장르와 현재 장르와 일치하면,

각 장르에서 최대 2곡을 뽑기 위해서 만든 리스트에 현재 인덱스를 추가한다.

 

Comparator<Integer> comparator = new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        // o1과 o2를 비교하는 비교 로직 작성
        // o1이 작을 경우 음수, 같을 경우 0, 큰 경우 양수를 리턴하도록 구현
        // 예: 오름차순으로 정렬할 경우 o1 - o2를 리턴

        //재생횟수가 같지 않으면 내림차순으로 정렬
        if (plays[o1] != plays[o2]) {
            return plays[o2] - plays[o1];
        }
        //재생횟수가 같으면 인덱스가 낮은 것부터
        return o1 -o2;
    }
};

Collections.sort(songsInGenre, comparator);

그리고 그 리스트 안에 들어간 요소를 정렬한다. 인덱스의 크기로 비교하는 게 아니라 

리스트에 들어간 인덱스에 해당하는 plays의 i번째를 기준으로 크기 비교를 해야한다.

 

단, 여기서 비교하려는 것들이 재생횟수가 같다면, 인덱스가 낮은 것이 앞으로 오는 것이 문제의 조건이었으니까 내림차순으로 한다.
그리고 재생횟수가 다르면, 재생횟수가 많은 것이 앞으로 와야하니 내림차순으로 정렬한다.

 

int maxSongsToPick = Math.min(2, songsInGenre.size());
for (int i = 0; i < maxSongsToPick; i++) {
    answerList.add(songsInGenre.get(i));
}

다음으로는 베스트 앨범에 들어갈 두 곡을 선정해야 한다.

 

다만 해당 장르에 곡이 한 곡일 수도 있다.

그러니 2와(최대 2곡이니까) songsInGenre리스트의 크기를 비교해서 for문의 범위를 잡아준다.

처음엔 삼항연산자를 썼는데, IDE에서 math.min으로 바꾸라고 해서 바꿨다 ㅠ

int maxSongsToPick = (songsInGenre.size() < 2) ? songsInGenre.size() : 2;

 

댓글