Создать объекты из списка строк с их элементами в качестве параметров

Windows 10;00001;2000;20;Microsoft
USB Flash Drive;20021;500;100;Samsung
Dell x-01;200232;1000;Notebook Dell;10;Dell
Dell od-1;3449;700;Monitor Dell;15;Dell
Asus x50m;4290;500;Notebook Asus;3;Asus

Читаю линии в списке, с помощью String split создаю массив строк. Создаю объекты new Product(), сеттерами поочередно внося строки в качестве параметров и приводя некоторые из них к необходимому типу.

В первом случае (строка Windows) все хорошо, во втором случае тоже, начиная со строки Dell и ниже есть дополнительная запись аля Notebook Dell, которую надо удалить или пропустить, чтобы корректно внести данные в конструктор. Я написал следующий код, благодаря которому все работает без лишней записи. Помогите добавить обработку:

private List<Product> getParsedList(List<String> listOfStrings) {
        List<Product> parsedList = new ArrayList<>();
        for (String line : listOfStrings) {
            String[] productFields = line.split(";");
            Product product = new Product();
            product.setName(productFields[0]);
            product.setUniqueNumber(productFields[1]);
            Integer price = Integer.parseInt(productFields[2]);
            product.setPrice(price);
            Integer count = Integer.parseInt(productFields[3]);
            product.setCount(count);
            product.setProduction(productFields[4]);

            parsedList.add(product);
        }
        return parsedList;
    }

И можно ли вообще добавить итерацию к элементам массива?


Ответы (1 шт):

Автор решения: Alex Rudenko

Если структура строки более-менее известна и определена, можно построить регулярное выражение с именованными группами, в котором будет пропущена дополнительная колонка или несколько колонок, и тогда можно будет вычитать данные после сопоставления строки заданному шаблону.

Шаблон может быть таким, группа (?<empty1>[^;]+;)* между группами price и count, содержащими целые числа, является необязательной и может содержать несколько колонок:

private static Pattern PRODUCT = Pattern.compile(
"(?<name>[^;]*);(?<id>[^;]*);(?<price>\\d+);(?<empty1>[^;]+;)*(?<count>\\d+);(?<production>[^;]*).*"
);

Допустим, что класс Product имеет конструктор со всеми необходимыми параметрами (например, сгенерированный при помощи Lombok аннотаций @Builder или @AllArgsConstructor), тогда данный метод можно переписать так:

private static Pattern PRODUCT = Pattern.compile(
"(?<name>[^;]*);(?<id>[^;]*);(?<price>\\d+);(?<empty1>[^;]+;)*(?<count>\\d+);(?<production>[^;]*).*"
);

private static List<Product> getParsedList(List<String> listOfStrings) {
    return listOfStrings
            .stream()  // Stream<String>
            .map(PRODUCT::matcher)    // Stream<Matcher>
            .filter(Matcher::matches) // выбрать совпавшие строки
            .map(m -> new Product(
                    m.group("name"),
                    m.group("id"),
                    Integer.parseInt(m.group("price")),
                    Integer.parseInt(m.group("count")),
                    m.group("production")
            )) // Stream<Product>
            .collect(Collectors.toList());
}

Тест (добавлена еще одна колонка после Notebook Dell):

List<String> data = Arrays.asList(
    "Windows 10;00001;2000;20;Microsoft",
    "USB Flash Drive;20021;500;100;Samsung",
    "Dell x-01;200232;1000;Notebook Dell;Another column;10;Dell",
    "Dell od-1;3449;700;Monitor Dell;15;Dell",
    "Asus x50m;4290;500;Notebook Asus;3;Asus"
);

List<Product> products = getParsedList(data);
products.forEach(System.out::println);

Результат:

Product(name=Windows 10, uniqueNumber=00001, price=2000, count=20, production=Microsoft)
Product(name=USB Flash Drive, uniqueNumber=20021, price=500, count=100, production=Samsung)
Product(name=Dell x-01, uniqueNumber=200232, price=1000, count=10, production=Dell)
Product(name=Dell od-1, uniqueNumber=3449, price=700, count=15, production=Dell)
Product(name=Asus x50m, uniqueNumber=4290, price=500, count=3, production=Asus)

Вариант с обычным for циклом:

private static List<Product> getParsedList(List<String> listOfStrings) {
    List<Product> parsedList = new ArrayList<>();
    for (String line : listOfStrings) {
        Matcher matcher = PRODUCT.matcher(line);
        if (matcher.matches()) {
            parsedList.add(new Product(
                    matcher.group("name"),
                    matcher.group("id"),
                    Integer.parseInt(matcher.group("price")),
                    Integer.parseInt(matcher.group("count")),
                    matcher.group("production")
            ));
            System.out.println("empty: " + matcher.group("empty1"));
        }
    }
    return parsedList;
}
→ Ссылка