4

I have a List<report> in Java and I want to change it to List<graph>

in case that

class report {
   String date;
   String type;
   String count;
}

class graph{
   String date;
   String numberA;
   String numberB;
   String numberC;
}

for example i have :

date    type  count     change it into >  date   numberA  numberB  numberC
03/21   A     8017                        03/21   8017     5019      0
03/21   B     5019                        03/22   5001     0         8870                          
03/22   A     5001                        03/23   0        8305      0  
03/22   C     8870                        03/24   1552     2351      8221  
03/23   B     8305
03/24   A     1552                          
03/24   B     2351                          
03/24   C     8221                         

I know that with some if (condition) and iteration it can be possible but is there another solution for this?

any answer will be appreciated.

Roozbe
  • 259
  • 2
  • 13
  • Can you just use a for loop for every element in your report list, then create a new graph list using the constructor? – cjnash Jun 12 '18 at 12:15
  • It would probably be better to just make a new `List` with `graph` objects instead. – 0xCursor Jun 12 '18 at 12:20
  • dear @cjnash I know that I can use for loop but I want another solution. – Roozbe Jun 12 '18 at 12:25
  • Dear @LAD yes , I should make new `List` of `grapgh`but i need new solution for that. – Roozbe Jun 12 '18 at 12:26
  • 1
    I think a mapper class would be a good choice in your case. [Design pattern to convert a class to another](https://stackoverflow.com/questions/11832076/design-pattern-to-convert-a-class-to-another?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa) – Arya Jun 12 '18 at 12:26
  • I recommend this blogpost, http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/ , might do a java streams answer later if no one beats me to it – Adam Jun 12 '18 at 12:29

2 Answers2

7

You want one Graph instance per a date. Thus, store Graph instances into a Map that keeps one Graph per a date. Iterate over the list of reports and build the contents of Graph (numberA, numberB, numberC) as Report instances come in, and based on the type they carry. Last, build a List<Graph> based on the entries of the map.

Using Java 8 (untested), streams and collect(), assuming reports is a List<Reports>:

    List<Graph> graphs = new ArrayList<>(reports  // we want a list - so let's create one. The rest of the code is just to give it some initial contents 
                    .stream()    // stream of reports
                    .collect(HashMap<String, Graph>::new, //collect them - start with a map of date -> grap
                    (map, report)->{ // here, for each report:
                         // pick a graph instance for the date, create one if it does not exist yet
                         Graph graph = map.computeIfAbsent(report.date, date -> new Graph(report.date));   
                         // Next, populate the graph instance based on report type
                         if ("A".equals(report.type)) { graph.numberA = report.count; }  
                         else if ("B".equals(report.type)) { graph.numberB = report.count; }
                         else if ("C".equals(report.type)) { graph.numberC = report.count; }
                        },
                    Map::putAll) // see collect() javadoc for a description of why this is here
                    .values());  // last step - get all values from the map (it's a Collection)

Edit: fixed compile errors, Edit 2: added code comments

Edit 3: The above code does not set "0" as default value in the Graphs (for the case a Report does not exist for a given date / type combination). I would suggest to handle this in the Graph class ( String numberA = "0" etc.). Otherwise, the default values would be nulls of course.

Roozbe
  • 259
  • 2
  • 13
david a.
  • 5,283
  • 22
  • 24
  • This is probably the most concise you can get make this. The only thing you might want to do differently is add a Graph constructor accepting a Report as its argument, and use the constructor in the stream. – Chris Jun 12 '18 at 12:43
  • @Chris, agree about the constructor. I was just working with what we were given :) – david a. Jun 12 '18 at 12:49
  • @davida. In your case when report.type is "A" the values of graph.numberB and graph.numberC will stays null – Beno Jun 12 '18 at 12:54
  • Thank you @david a. for your answer, I just initialize `new Graph()` in your code. It works fine now. – Roozbe Jun 13 '18 at 05:56
4

We have stream solution from @DavidA. What about mix one, I think it could be more clear to realize, with same result:

Report class:

class Report {

    public static final String TYPE_A = "A";
    public static final String TYPE_B = "B";
    public static final String TYPE_C = "C";

    private final String date;
    private final String type;
    private final String count;
}

Graph class:

class Graph {

    private final String date;

    private String numberA = "0";
    private String numberB = "0";
    private String numberC = "0";

    void addReport(Report report) {
        if (report == null || !report.getDate().equals(date))
            return;

        String type = report.getType();
        String count = report.getCount();

        if (Report.TYPE_A.equals(type ))
            numberA = count ;
        else if (Report.TYPE_B.equals(type ))
            numberB = count ;
        else if (Report.TYPE_C.equals(type ))
            numberC = count ;
    }
}

Convert method:

public static List<Graph> convertToGraphs(List<Report> reports) {
    Map<String, Graph> map = new TreeMap<>();

    reports.forEach(report -> {
        String date = report.getDate();
        Graph graph = map.get(date);

        if (graph == null)
            map.put(date, graph = new Graph(date));

        graph.addReport(report);
    });

    return new ArrayList<>(map.values());
}
Oleg Cherednik
  • 17,377
  • 4
  • 21
  • 35
  • Thank you @oleg.cherednik for your correct answer. I test it and just your `Graph()` class need a constructor for initializing `date`. I saw that you erased it! – Roozbe Jun 13 '18 at 05:59
  • @Roozbe yes. This is obvious and I made my answer a little bit shorter. – Oleg Cherednik Jun 13 '18 at 06:17