Java Streams - Map two string lines each to one object

Viewed 198

Say we have a text file that contains (product name, price) pairs. Each pair occupies two lines in the text file where the first line corresponds to the product name, and the second line corresponds to the price of that product. We may assume the text file is in the right format (and has an even amount of lines)

Example:

Ice Cream
$3.99
Chocolate
$5.00
Nice Shoes
$84.95
...

Now I have a simple class representing such pairs:

public class Product {
    private final String name;
    private final int price;

    public Product(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return this.name;
    }

    public int getPrice() {
        return this.price;
    }
}

We read the file containing the pairs and now have a string array containing all the individual lines. I need to use Streams to map each two lines to one object of type Product.

How can I group two lines each and then map them to a Product? If there is a simple approach, does it still work with parallel streams?

2 Answers

You can make your own Collector which temporarily stores the previous element/string. When the current element starts with a $, the name of the product is stored in prev. Now you can convert the price to a double and create the object.

private class ProductCollector {

    private final List<Product> list = new ArrayList<>();

    private String prev;

    public void accept(String str) {
        if (prev != null && str.startsWith("$")) {
            double price = Double.parseDouble(str.substring(1));
            list.add(new Product(prev, price));
        }
        prev = str;
    }

    public List<Product> finish() {
        return list;
    }

    public static Collector<String, ?, List<Product>> collector() {
        return Collector.of(ProductCollector::new, ProductCollector::accept, (a, b) -> a, ProductCollector::finish);
    }
}

Since you need to rely on the sequence (line with price follows line with name), the stream cannot be processed in parallel. Here is how you can use your custom collector:

String[] lines = new String[]{
        "Ice Cream", "$3.99",
        "Chocolate", "$5.00",
        "Nice Shoes", "$84.95"
};

List<Product> products = Stream.of(lines)
        .sequential()
        .collect(ProductCollector.collector());

Note that your prices are not integers which is why I used a double to represent them properly.

if you have the items in an array you can use vanilla java with an intStream and filter on even values and then in the next map you can use index and index+1. Maybe have look here

Related