diff --git a/README.md b/README.md index db5a50b4..bff9430e 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,61 @@ 4. Найденная в соответствии с условием задачи категория должна выводиться в изначальном наименовании, приведенном в файле с входными данными. Если таких категорий несколько, то на вывод они все подаются в алфавитном порядке. ## Автор решения - +Корчагин Илья Вадимович ## Описание реализации +На основе json файла были созданы классы POJO: Response, Order, Item, и Category. При помощи библиотеки Jackson считывается +json из файла и преобразуется в экземпляры этих классов. Для работы с json был создан класс JsonConverter в пакете utils. +В этом классе метод _convertJsonToResponse_ преобразует json файл в объект класса Response. Метод _convertCategoriesToJson_ +преобразует список категорий в формат json. + +В классе ReportGenerator находится функционал для генерации отчета по популярным категориям. Метод _getPopularCategory_ +принимает JSON-файл и возвращает список категорий с максимальным количеством упоминаний. Для работы со списками +используется Stream API, т.к. он позволяет сократить код и упростить работу с коллекциями. + +Часть кода в методе _getPopularCategory_, отвечающая за подсчет количества упоминаний категорий. + +```java +Response response = JsonConverter.convertJsonToResponse(json); +Map categoriesCount = new HashMap<>(); + +response.getOrders().stream() +// Отбираем только заказы сделанные в декабре +.filter(order -> checkNewYearHolidays(order.getOrderedAt())) + .forEach(order -> { + order.getItems().forEach(item -> { + categoriesCount.put(item.getCategory().getName(), + // Суммируем количество упоминаний по категориям + categoriesCount.getOrDefault(item.getCategory().getName(), 0) + 1); + }); + }); +``` + +Часть кода в методе _getPopularCategory_, отвечающая за определение самых популярных категорий. Сначала определяется +максимальное число упоминания категории, а затем выбираются все названия категорий, число упоминания которых равно +максимальному. Полученный список сортируется в алфавитном порядке. + +```java +// Получаем максимальное количество упоминаний +long maxCount = categoriesCount.values().stream().max(Integer::compareTo).orElse(0); + +// Возвращаем список категорий с максимальным количеством упоминаний +return categoriesCount.entrySet().stream() + .filter(entry -> entry.getValue() == maxCount) // Фильтруем все элементы категории с максимальным количеством упоминаний + .map(Map.Entry::getKey) + .sorted() // Сортируем по алфавиту + .collect(Collectors.toList()); +``` + + +В методе _getPopularCategory_ используются приватные методы _parseDate_ для преобразования даты из текста в объект класса +_LocalDateTime_. Метод _checkNewYearHolidays_ проверяет является ли дата предновогодней (1 - 31 декабря). В этот метод +можно было бы передавать номер месяца и проверять принадлежность даты к этому месяцу, чтобы не хардкодить декабрь. + +Метод _generateReport_ преобразует список категорий в строку формата json. ## Инструкция по сборке и запуску решения +Для сборки приложения необходимо в командной строке использовать команду javac, чтобы скомпилировать проект. Затем запустить скомпилированный класс App при помощи команды java и в качестве аргумента передать путь к json файлу. + +Можно использовать maven плагин jar для создания jar файла. Созданный файл запустить при помощи команды java -jar [название файла].jar [путь к json файлу] аргументом является путь к json файлу. + +Или запустить загруженный файл croc.jar при помощи команды java -jar [путь к файлу croc.jar] [путь к файлу json] diff --git a/croc.jar b/croc.jar new file mode 100644 index 00000000..c7afd002 Binary files /dev/null and b/croc.jar differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..850866a4 --- /dev/null +++ b/pom.xml @@ -0,0 +1,86 @@ + + + 4.0.0 + ru + croc + 0.0.1-SNAPSHOT + Croc + Croc + + 17 + + + + com.fasterxml.jackson.core + jackson-core + 2.17.0 + + + com.fasterxml.jackson.core + jackson-databind + 2.17.0-rc1 + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-dependencies + prepare-package + + copy-dependencies + + + + ${project.build.directory}/libs + + + + + + + maven-assembly-plugin + + + + true + libs/ + ru.croc.App + + + + jar-with-dependencies + + + + + package + + single + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + + true + libs + + ru.croc.App + + + + + + + + \ No newline at end of file diff --git a/src/main/java/ru/croc/App.java b/src/main/java/ru/croc/App.java new file mode 100644 index 00000000..ee29fa8d --- /dev/null +++ b/src/main/java/ru/croc/App.java @@ -0,0 +1,13 @@ +package ru.croc; + +import ru.croc.utils.ReportGenerator; + +import java.io.File; + +public class App { + + public static void main(String[] args) { + System.out.println(ReportGenerator.generateReport( + ReportGenerator.getPopularCategory(new File(args[0])))); + } +} diff --git a/src/main/java/ru/croc/models/Category.java b/src/main/java/ru/croc/models/Category.java new file mode 100644 index 00000000..ea9ab1dd --- /dev/null +++ b/src/main/java/ru/croc/models/Category.java @@ -0,0 +1,24 @@ +package ru.croc.models; + +public class Category{ + + private String id; + + private String name; + + public void setName(String name){ + this.name = name; + } + + public String getName(){ + return name; + } + + public void setId(String id){ + this.id = id; + } + + public String getId(){ + return id; + } +} diff --git a/src/main/java/ru/croc/models/Item.java b/src/main/java/ru/croc/models/Item.java new file mode 100644 index 00000000..7b63be9a --- /dev/null +++ b/src/main/java/ru/croc/models/Item.java @@ -0,0 +1,34 @@ +package ru.croc.models; + +public class Item { + + private String id; + + private String name; + + private Category category; + + public void setName(String name){ + this.name = name; + } + + public String getName(){ + return name; + } + + public void setId(String id){ + this.id = id; + } + + public String getId(){ + return id; + } + + public void setCategory(Category category){ + this.category = category; + } + + public Category getCategory(){ + return category; + } +} diff --git a/src/main/java/ru/croc/models/Order.java b/src/main/java/ru/croc/models/Order.java new file mode 100644 index 00000000..d346f77c --- /dev/null +++ b/src/main/java/ru/croc/models/Order.java @@ -0,0 +1,29 @@ +package ru.croc.models; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +public class Order { + + @JsonProperty("ordered_at") + private String orderedAt; + + private List items; + + public void setOrderedAt(String orderedAt){ + this.orderedAt = orderedAt; + } + + public String getOrderedAt(){ + return orderedAt; + } + + public void setItems(List items){ + this.items = items; + } + + public List getItems(){ + return items; + } +} \ No newline at end of file diff --git a/src/main/java/ru/croc/models/Response.java b/src/main/java/ru/croc/models/Response.java new file mode 100644 index 00000000..c2609529 --- /dev/null +++ b/src/main/java/ru/croc/models/Response.java @@ -0,0 +1,22 @@ +package ru.croc.models; + +import java.util.List; + +public class Response{ + private List orders; + + public Response() { + } + + public Response(List orders) { + this.orders = orders; + } + + public void setOrders(List orders){ + this.orders = orders; + } + + public List getOrders(){ + return orders; + } +} \ No newline at end of file diff --git a/src/main/java/ru/croc/utils/JsonConverter.java b/src/main/java/ru/croc/utils/JsonConverter.java new file mode 100644 index 00000000..e3e93f46 --- /dev/null +++ b/src/main/java/ru/croc/utils/JsonConverter.java @@ -0,0 +1,33 @@ +package ru.croc.utils; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import ru.croc.models.Order; +import ru.croc.models.Response; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +public class JsonConverter { + private static final ObjectMapper mapper = new ObjectMapper(); + + public static Response convertJsonToResponse(File json) { + Response response = null; + try { + // Корневой элемент JSON'а это массив заказов, поэтому преобразуем его в список + response = new Response(mapper.readValue(json, new TypeReference>() {})); + } catch (IOException e) { + throw new RuntimeException(e); + } + return response; + } + + public static String convertCategoriesToJson(List categories) { + return "{categories: " + categories + "}"; + } + + public static ObjectMapper getObjectMapper() { + return mapper; + } +} diff --git a/src/main/java/ru/croc/utils/ReportGenerator.java b/src/main/java/ru/croc/utils/ReportGenerator.java new file mode 100644 index 00000000..3dc6a70c --- /dev/null +++ b/src/main/java/ru/croc/utils/ReportGenerator.java @@ -0,0 +1,70 @@ +package ru.croc.utils; + +import ru.croc.models.Response; + +import java.io.File; +import java.time.LocalDateTime; +import java.time.format.DateTimeParseException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class ReportGenerator { + /** + * Метод принимает JSON-файл и возвращает список категорий с максимальным количеством упоминаний + * @param json файл в формате JSON + * @return список категорий + */ + public static List getPopularCategory(File json) { + if (json == null) { + throw new IllegalArgumentException("JSON файл не существует"); + } + + Response response = JsonConverter.convertJsonToResponse(json); + Map categoriesCount = new HashMap<>(); + + // Считаем количество упоминаний категорий + response.getOrders().stream() + .filter(order -> checkNewYearHolidays(order.getOrderedAt())) + .forEach(order -> { + order.getItems().forEach(item -> { + categoriesCount.put(item.getCategory().getName(), + // Суммируем количество упоминаний по категориям + categoriesCount.getOrDefault(item.getCategory().getName(), 0) + 1); + }); + }); + + // Получаем максимальное количество упоминаний + long maxCount = categoriesCount.values().stream().max(Integer::compareTo).orElse(0); + + // Возвращаем список категорий с максимальным количеством упоминаний + return categoriesCount.entrySet().stream() + .filter(entry -> entry.getValue() == maxCount) // Фильтруем все элементы категории с максимальным количеством упоминаний + .map(Map.Entry::getKey) + .sorted() // Сортируем по алфавиту + .collect(Collectors.toList()); + } + + public static String generateReport(List categories) { + return JsonConverter.convertCategoriesToJson(categories); + } + + private static LocalDateTime parseDate(String date) { + return LocalDateTime.parse(date); + } + + /** + * Проверяет является ли дата предновогодней (1 - 31 декабря) + * @param date дата в формате "yyyy-MM-dd" + * @return true - является, false - не является + */ + private static boolean checkNewYearHolidays(String date) { + try { + LocalDateTime orderDate = parseDate(date); + return orderDate.getMonthValue() == 12; + } catch (DateTimeParseException e) { + return false; + } + } +} diff --git a/src/main/resources/lib/byte-buddy-1.14.9.jar b/src/main/resources/lib/byte-buddy-1.14.9.jar new file mode 100644 index 00000000..ea387ae3 Binary files /dev/null and b/src/main/resources/lib/byte-buddy-1.14.9.jar differ diff --git a/src/main/resources/lib/jackson-annotations-2.17.0-rc1.jar b/src/main/resources/lib/jackson-annotations-2.17.0-rc1.jar new file mode 100644 index 00000000..36fe9891 Binary files /dev/null and b/src/main/resources/lib/jackson-annotations-2.17.0-rc1.jar differ diff --git a/src/main/resources/lib/jackson-core-2.17.0.jar b/src/main/resources/lib/jackson-core-2.17.0.jar new file mode 100644 index 00000000..00921a37 Binary files /dev/null and b/src/main/resources/lib/jackson-core-2.17.0.jar differ diff --git a/src/main/resources/lib/jackson-databind-2.17.0-rc1.jar b/src/main/resources/lib/jackson-databind-2.17.0-rc1.jar new file mode 100644 index 00000000..97c0f220 Binary files /dev/null and b/src/main/resources/lib/jackson-databind-2.17.0-rc1.jar differ