Loading...

Във форума е въведено ограничение, което позволява на потребителите единствено да разглеждат публикуваните въпроси.

SimeonKazandzhiev avatar SimeonKazandzhiev 3 Точки

Plant Discovery от Final Exam

Моля за малко помощ, докарвам логиката но в принтирането се спъвам .

Ако някой има решение на 100/100 моля да сподели .

Ето го и кода ми:

https://pastebin.com/iQqrBpRv

Благодаря предварително

Тагове:
0
Programming Fundamentals
MartinBG avatar MartinBG 4803 Точки

Имаше няколко пропуска по условието, като например да се извежда "error" при невалидна команда, както и да се презаписва rarity, ако растението бъде въведено повече от веднъж в началото.

 

Тази задача би се решила доста по-лесно и прегледно с помощен клас, но ето и оправеното решение с два Map-а :

 

import java.util.*;

public class PlantDiscovery {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        Map<String, Integer> plants = new HashMap<>();
        Map<String, List<Double>> rating = new HashMap<>();

        int n = Integer.parseInt(scanner.nextLine());

        for (int i = 0; i < n; i++) {
            String[] input = scanner.nextLine().split("<->");
            String plant = input[0];
            int rarity = Integer.parseInt(input[1]);
            plants.compute(plant, (k, v) -> rarity);
            rating.putIfAbsent(plant, new ArrayList<>());
        }

        String input;
        while (!"Exhibition".equals(input = scanner.nextLine())) {
            String[] tokens = input.split(": ");
            String command = tokens[0];
            String[] elements = tokens[1].split(" - ");
            String name = elements[0];

            if (!plants.containsKey(name)) {
                System.out.println("error");
                continue;
            }

            switch (command) {
            case "Rate":
                double rate = Double.parseDouble(elements[1]);
                rating.get(name).add(rate);
                break;
            case "Update":
                int rarity = Integer.parseInt(elements[1]);
                plants.compute(name, (k, v) -> rarity);
                break;
            case "Reset":
                rating.get(name).clear();
                break;
            default:
                System.out.println("error");
            }
        }

        System.out.println("Plants for the exhibition:");
        plants.entrySet()
                .stream()
                .sorted(Map.Entry.<String, Integer>comparingByValue()
                        .thenComparingDouble(x -> rating.get(x.getKey()).stream()
                                .mapToDouble(Double::doubleValue)
                                .average().orElse(0.0))
                        .reversed())
                .forEach(e -> System.out.printf("- %s; Rarity: %d; Rating: %.2f%n", e.getKey(), e.getValue(),
                        rating.get(e.getKey()).stream().mapToDouble(Double::doubleValue).average().orElse(0.0)));
    }
}

 

0
SimeonKazandzhiev avatar SimeonKazandzhiev 3 Точки

Благодаря ти много , да знаех че нямах случая за "error", но ме чупеше принтирането и сортирането едновременно на двата мапа,понеже никъде не го бяха показвали и не успях да намеря инфо в интернет.Интересно,досега не бях използвал "compute".Би ли ми обяснил накратко в кои случаи е удобно да се използва.:)

1
MartinBG avatar MartinBG 4803 Точки

@SimeonKazandzhiev

 

Извинявам се за закъснелия отговор.

 

Map#compute(K, BiFunction(K, V)) ни дава възможност едновременно да достъпим и да променим стойността на вече съществуващ ключ. Това е полезно, например, ако искаме да конкатенираме стрингове, както е показано и в документацията:

Attempts to compute a mapping for the specified key and its current mapped value (or null if there is no current mapping). For example, to either create or append a String msg to a value mapping:

 
 map.compute(key, (k, v) -> (v == null) ? msg : v.concat(msg))

 

Друго приложение е, ако искаме да сумираме старата стойност с нова. Например, ако в конкретната задача се искаше при повторно въвеждане на растение новото rarity да се добави към вече съществуващото:

plants.compute(plant, (k, v) -> Objects.requireNonNullElse(v, 0) + rarity);

Забележете, че задължително трябва да се прави проверка за null на старата стойност, преди тя да бъде използвана (ще е null ако растението се въвежда за първи път или ако по някаква причина е имало стойност null). Може да се използва тернарен оператор (примера от официалната документация) или Objects#requireNonNullElse​(T obj, T defaultObj) - и двете правят едно и също в случая.

 

След като изяснихме това, вече сигурно се досещате, че използването на compute в конкретната задача е абсолютно ненужно (и нежелателно) и може да се заменени с Map#put​(K key, V value), което ни дава желаната функционалност, без да събужда въпроси какво прави и защо е използвано (т.е. допринася за четимостта на кода) blush

plants.put(plant, rarity);

 

1
18/11/2020 10:30:02
Georgieva_Nadezhda avatar Georgieva_Nadezhda 28 Точки

Здравей MartinBG,

Можеш ли да напишеш на части разбито по-подробно какво се први в тази част на сортирането без да се ползват готовите методи Map.Entry.<String, Integer>comparingByValue() .thenComparingDouble. Благодаря ти. Разбирам какво е сортирането спрямо уловието, но синтаксиса ми е доста сложен към момента и ако знаеш нещо по-лесно, много ще ми помогнеш :) 

.sorted(Map.Entry.<String, Integer>comparingByValue() .thenComparingDouble(x -> rating.get(x.getKey()).stream() .mapToDouble(Double::doubleValue) .average().orElse(0.0)) .reversed())

1
MartinBG avatar MartinBG 4803 Точки

@Georgieva_Nadezhda

Както писах и по-горе, решението с използване на 2 Map-а не е оптимално, защото кодът е по-труден за четене, поддръжка и модифициране (например, ако трябва да добавим нов параметър към всяко растение или пък да пазим списък с имената на хората, които вече са оценили растението).

Сложното сортиране (като запис и синтаксис) е следствие от горното, защото трябва да се използват и двата мапа:

.sorted(Map.Entry.<String, Integer>
             comparingByValue() // първо сортираме по стойност, т.е rarity
            .thenComparingDouble( // после сортираме по double стойност, която получаваме
                x -> rating.get(x.getKey()).stream() // вземайки рейтингите от другия мап
                     .mapToDouble(Double::doubleValue) // to DoubleStream
                     .average() // Optional<Double> - средна стойност
                     .orElse(0.0)) // връща средната стойност или 0, при празен лист 
             .reversed() // обръща реда на предходната сортировка (по средна стойност)
             )

Решението, при което се използва помощен клас Plant би решило тези проблеми и ще улесни сортировката, защото ще имаме само един Map<String, Plant> plants:

    
    public static void main(String[] args) {

        //.....

        System.out.println("Plants for the exhibition:");
        plants.values()
                .stream()
                .sorted(Comparator
                        .comparing(Plant::getRarity)
                        .thenComparingDouble(Plant::getAverageRating)
                        .reversed())
                .map(Plant::info)
                .forEach(System.out::println);
    }

    private static final class Plant {
        private final String name;
        private int rarity;
        private final List<Double> ratings;

        public Plant(String name, int rarity) {
            this.name = name;
            this.rarity = rarity;
            ratings = new ArrayList<>();
        }

        public void addRating(double rating) {
            ratings.add(rating);
        }

        public void setRarity(int newRarity) {
            rarity = newRarity;
        }

        public void clearRatings() {
            ratings.clear();
        }

        public int getRarity() {
            return rarity;
        }

        public String getName() {
            return name;
        }

        public String info() {
            return String.format(Locale.US, "- %s; Rarity: %d; Rating: %.2f", name, rarity, getAverageRating());
        }

        public double getAverageRating() {
            return ratings.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
        }
    }

 

2
Georgieva_Nadezhda avatar Georgieva_Nadezhda 28 Точки

Добро утро, @MartinBG, много ти благодаря за насоките, чудех се как е най-добре да се подхожда към такива задачи, за които аз си мисля, че ми трябват 2 мапа, а то всъщност мап с клас е правилния подход. Почвам да пренаписвам няколко задачки сега. Благодаря за разясненията още веднъж. Хубав ден 🙃

1
Можем ли да използваме бисквитки?
Ние използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Можете да се съгласите с всички или част от тях.
Назад
Функционални
Използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Използваме „сесийни“ бисквитки, за да Ви идентифицираме временно. Те се пазят само по време на активната употреба на услугите ни. След излизане от приложението, затваряне на браузъра или мобилното устройство, данните се трият. Използваме бисквитки, за да предоставим опцията „Запомни Ме“, която Ви позволява да използвате нашите услуги без да предоставяте потребителско име и парола. Допълнително е възможно да използваме бисквитки за да съхраняваме различни малки настройки, като избор на езика, позиции на менюта и персонализирано съдържание. Използваме бисквитки и за измерване на маркетинговите ни усилия.
Рекламни
Използваме бисквитки, за да измерваме маркетинг ефективността ни, броене на посещения, както и за проследяването дали дадено електронно писмо е било отворено.