domingo, 7 de febrero de 2021

Using flatMap to extract nested elements from a List

Scenario

We need to extract the nested elements from a list of lists based on levels. To do so we can use the flatMap functionality from Java Lambda expressions.

Solution

First we need a data model, in this case we will use a Music Library example based on Bands that have Records that have Songs:

Music Library:

@Getter
@Setter
@ToString
public class MusicLibrary {

    private List<Band> bands;

    public MusicLibrary(List<Band> bands) {
        this.bands = bands;
    }

    public void addBand(Band band){
        if(band != null){
            this.bands.add(band);
        }
    }
}

Band:

@Getter
@ToString
public class Band {

    private final String name;
    private final List<Record> records;

    public Band(String name) {
        this.name = name;
        this.records = new ArrayList<>();
    }

    public void addRecord(Record record){
        if(record != null){
            this.records.add(record);
        }
    }
}

Record:

@Getter
@Setter
@AllArgsConstructor
public class Record {

    private String name;
    private Integer year;
    private List<Song> songs;
}

Song:

@Getter
@Setter
public class Song {

    private String name;

    public Song(String name) {
        this.name = name;
    }
}

A quick Music Library for tests We will have a simple music library formed by two bands (Nirvana and Soundgarden) with 3 records and 9 songs in total:

    private MusicLibrary musicLibraryTest(){

        Band nirvana = new Band("Nirvana");

        Record bleach = new Record(
                "Bleach",
                1989,
                List.of(
                        new Song("Love Buzz"),
                        new Song("Negative Creep"))
        );

        Record nevermind = new Record(
                "Nevermind",
                1991,
                List.of(
                        new Song("Smells like teen spirit"),
                        new Song("Lithium"),
                        new Song("Something in the way"))
        );

        nirvana.addRecord(bleach);
        nirvana.addRecord(nevermind);

        Band soundgarden = new Band("Soundgarden");

        Record superunknown = new Record(
                "Superunknown",
                1994,
                List.of(
                        new Song("Fell on black days"),
                        new Song("Black hole sun"),
                        new Song("The day I tried to live"),
                        new Song("4th of July")
                )
        );

        soundgarden.addRecord(superunknown);

        return new MusicLibrary(List.of(nirvana, soundgarden));

    }

Now, let's see how to use the flatMap method from Java Lambda streams that will allow us to access to the different levels of the nested objects in our data structure:

Accessing to first level Records

@Test
void extractRecords(){

    List<Record> records = musicLibraryTest().getBands()
            .stream()
            .flatMap(band -> band.getRecords().stream())
            .collect(Collectors.toList());

    Assertions.assertThat(records).size().isEqualTo(3);
    System.out.println(records.toString());
}

The output will be:

[
     Record{
        Bleach',
        1989,
        [
            Love Buzz, 
            Negative Creep]
    }, 
     Record{
        Nevermind',
        1991,
        [
            Smells like teen spirit, 
            Lithium, 
            Something in the way]
    }, 
     Record{
        Superunknown',
        1994,
        [
            Fell on black days, 
            Black hole sun, 
            The day I tried to live, 
            4th of July]
    }]

Accessing to the second level Songs

To get all the songs of our library we can use again the flatMap method to access to the second level of nested data in our structure:

@Test
void extractSongs(){

    List<Song> songs = musicLibraryTest().getBands()
            .stream()
            .flatMap(band -> band.getRecords().stream())
            .flatMap(record -> record.getSongs().stream())
            .collect(Collectors.toList());

    Assertions.assertThat(songs).size().isEqualTo(9);
    System.out.println(songs);
}

The output will show the complete list of songs of our library:

[
            Love Buzz, 
            Negative Creep, 
            Smells like teen spirit, 
            Lithium, 
            Something in the way, 
            Fell on black days, 
            Black hole sun, 
            The day I tried to live, 
            4th of July
]

Using flatMap to extract nested elements from a List

Scenario We need to extract the nested elements from a list of lists based on levels. To do so we can use the flatMap functionality from ...