Search code examples
javafilejava-stream

Parsing comma-delimited text file with alternating data rows using the Java stream API


I have this file to process and I'm trying to figure out how to read the second line as a different object. At the end of the row, the last item is the number of buses. Second line, fourth line and so on should contain idBus and noOfBus separated by commas.

1,Harvard University,2
1,140,2,56
2,Massachusetts Institute of Technology,2
1,240,5,56
3,University of California Berkeley,3
1,112,2,56,3,28
4,Columbia University,4
1,84,2,84,3,84,7,28

Based on these classes

class University {
   public int idUniversity;
   public String nameOfUniversity;
   public int noOfBuses;
}
class Bus {
   public int idBus;
   public int noOfBus;
}

I want a function to process a List for all the bus and university lines from the CSV file. It must use the stream API. I don't know if it's possible or how to proceed.

Of course, changes can be made based on classes but keep the variables and not add anything extra.

I think the most convenient thing would be to go through the file and get two lists based on the two classes (University and Bus) and finally add them in a THIRD list and then to do the processing but I don't know how.

Based on this file I have to do processing with the stream API such as "sort buses by ID" or "which universities have more busses?" or "which bus goes to several universities?".

That's what I've been trying to do. Normally this is how I would have gone through a CSV file.

private static List<University> readListOfUniversities() throws IOException {
    try (BufferedReader bufferedReader = new BufferedReader(new FileReader(PATH_UNIVERSITIES_TEXT_FILE))) {
        return bufferedReader.lines().map(line -> {
            String[] tokens = line.split(",");
            var idUniversity = Integer.parseInt(tokens[0]);
            var nameOfUniversity = tokens[1].trim();
            var noOfBuses = Integer.parseInt(tokens[2]);
            var university = new University(idUniversity, nameOfUniversity, noOfBuses);
            return university;
        }).collect(Collectors.toList());
    }
}

Like this one:

1,Harvard University,2
2,Massachusetts Institute of Technology,2
3,University of California Berkeley,3
4,Columbia University,4

Solution

  • Here's how I would do it (I made slight modifications to your classes)

    public class University {
        private int id;
        private String name;
        private List<Bus> busList;
        //let the constructor do the work
        public University(String [] data, List<Bus> busList) {
            id = Integer.parseInt(data[0]);
            name = data[1];
            this.busList = busList; 
        }
    
        public int getId() {
            return id;
        }
    
        public String getName() {
            return name;
        }
    
        public List<Bus> getBusList() {
            return busList;
        }
    
        @Override
        public String toString() {
            return "University [id=" + id + ", name=" + name + ", busList=" + busList + "]";
        }
        
    }
    
    public class Bus {
        private int id;
        private int num;
        
        public Bus(int id, int num) {
            this.id = id;
            this.num = num;
        }
        
        public int getId() {
            return id;
        }
    
        public int getNum() {
            return num;
        }
        //Method to parse the data into usuable objects
        public static List<Bus> generateBusInfo(String [] data) {
            List<Bus> buses = new ArrayList<>();
            for (int i = 0; i < data.length-1; i+=2) {
                int id = Integer.parseInt(data[i]);
                int num = Integer.parseInt(data[i+1]);
                buses.add(new Bus(id, num));
            }
            return buses;
        }
    
        @Override
        public String toString() {
            return "Bus [id=" + id + ", num=" + num + "]";
        }
    }
    
    
    public static void main(String[] args) throws IOException {
        //This is just so I dont have to bother with creating a file.
        String input = "1,Harvard University,2\n" + 
                "1,140,2,56\n" + 
                "2,Massachusetts Institute of Technology,2\n" + 
                "1,240,5,56\n" + 
                "3,University of California Berkeley,3\n" + 
                "1,112,2,56,3,28\n" + 
                "4,Columbia University,4\n" + 
                "1,84,2,84,3,84,7,28\n";
        List<University> universities = new ArrayList<>();
        //replace StringReader with a FileReader to the path of your file.
        try(BufferedReader reader = new BufferedReader(new StringReader(input))) {
            //read the first line
            String line = reader.readLine();
            while (line != null) { //keep reading until you're done
                String [] uniData = line.split(","); //convert string into an array
                line = reader.readLine(); //read the next line
                if (line != null) {
                    String [] busData = line.split(","); //convert
                    List<Bus> busList = Bus.generateBusInfo(busData); //make busses
                    universities.add(new University(uniData, busList)); //create university and add it to the list
                    line = reader.readLine(); //read the next line
                }
            }
        }
        
        //output the list
        for (University uni : universities) {
            System.out.println(uni);
        }
    }
    

    You could also read the Universities and buses into Maps for easier access.