3

I have a CSV file which has a header in the first line. I want to convert it to List<Map<String, String>>, where each Map<String, String> in the list represents a record in the file. The key of the map is the header and the value is the actual value of the field. What I have so far:

BufferedReader br = <handle to file>;
// Get the headers to build the map.
String[] headers = br.lines().limit(1).collect(Collectors.toArray(size -> new String[size]));
Stream<String> recordStream = br.lines().skip(1);

What further operations can I perform on recordStream so that I can transform it to List<Map<String, String>>?

Sample CSV file is:

header1,header2,header3   ---- Header line
field11,field12,field13   ----> need to transform to Map where entry would be like header1:field11 header2:field12 and so on.
field21,field22,field23
field31,field32,field33

Finally all these Maps need to be collected to a List.

4
  • 1
    Could you post a sample CSV file with a sample output? I'm not sure I understand what should happen. Commented Oct 13, 2015 at 15:58
  • 1
    You can't skip the first line like that. You need the names of the columns. Don't use streams for this. Commented Oct 13, 2015 at 16:05
  • So is it not possible to do it with streams? Commented Oct 13, 2015 at 16:13
  • 1
    You can retrieve the header line with an ordinary br.readLine() invocation, followed by processing the remaining lines via br.lines(). stream ops. Commented Oct 13, 2015 at 16:14

3 Answers 3

9

The following will work. The header line is retrieved by calling readLine directly on the BufferedReader and by splitting around ,. Then, the rest of the file is read: each line is split around , and mapped to a Map with the corresponding header.

try (BufferedReader br = new BufferedReader(...)) {
    String[] headers = br.readLine().split(",");
    List<Map<String, String>> records = 
            br.lines().map(s -> s.split(","))
                      .map(t -> IntStream.range(0, t.length)
                                         .boxed()
                                         .collect(toMap(i -> headers[i], i -> t[i])))
                      .collect(toList());
    System.out.println(headers);
    System.out.println(records);
};

A very important note here is that BufferedReader.lines() does not return a fresh Stream when it is called: we must not skip 1 line after we read the header since the reader will already have advanced to the next line.

As a side note, I used a try-with-resources construct so that the BufferedReader can be properly closed.

Sign up to request clarification or add additional context in comments.

4 Comments

the i in toMap function needs explicit typecast to Integer. This worked for me .collect(toMap((Integer i) -> headers[i], i -> t[i])))
As a side note I would still recommend to use libraries like Commons CSV to read CSV. CSV format supports quoting which allows having a comma inside the field. The provided implementation would read such files incorrectly.
@VivekKothari Maybe this is a bug of the IDE you are using. The code given in the answer compiles fine with Eclipse Mars and javac 1.8.0_51
I am using eclipse luna and java version "1.8.0_40" Java(TM) SE Runtime Environment (build 1.8.0_40-b25) Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode). But I am getting the error.
3

I know this is a bit of an old question, but I ran into the same problem, and created a quick sample of the Commons CSV solution mentioned by Tagir Valeev:

            Reader in = new FileReader("path/to/file.csv");
            Iterable<CSVRecord> records = CSVFormat.RFC4180.withFirstRecordAsHeader().parse(in);

            List<Map> listOfMaps = new ArrayList<>();
            for (CSVRecord record : records) {
                listOfMaps.add(record.toMap());
            }

Comments

1

You can use CsvMapper to convert CSV data to List<Map<String,String>>

import com.fasterxml.jackson.databind.MappingIterator;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;

public List<Map<String,String>> getCsvData() throws IOException {
    BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("your_path"),"SHIFT-JIS"));
    CsvMapper csvMapper = new CsvMapper();
    CsvSchema csvSchema = csvMapper.typedSchemaFor(Map.class).withHeader();
    MappingIterator<Map<String, String>> it = csvMapper.readerFor(Map.class).with(csvSchema.withColumnSeparator(CsvSchema.DEFAULT_COLUMN_SEPARATOR)).readValues(br);
    return it.readAll();
}

2 Comments

Is it this class? javadoc.io/doc/com.fasterxml.jackson.dataformat/… Your answer could be improved by clarifying/linking the specific class (the java ecosystem often has multiple things with equal or similar names doing similar things in the style of a different decade)
That's right, it's from com.fasterxml.jackson.dataformat.csv

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.