Reputation: 1152
I know this question can be misleading, if possible someone may correct it, I am not sure how to ask a question with this situation.
I am trying to Convert Class X into Class Y (here class Y contains fields of class X, but in different way, Eg:- Integer a, b;
inside class X , converts to Map<a, b>
in class Y while other variables stay the same.),
using streams in java 8. I am trying to return the final object to the UI part as json. The project is done in spring boot. Both Class X and Y contains the same objects, but Class Y is to make class X distinct I am not familiar with streams.
Class X
public class X {
private final String thumbnailUrl;
private final Integer duration;
private final String contentId;
private final Date reportDate;
private final Integer count;
public X(Report report, int count) {
this.thumbnailUrl = report.getContent().getThumbnailUrl();
this.duration = report.getContent().getDuration();
this.contentId = report.getContent().getContentId();
this.reportDate = report.getReportDate();
this.count = count;
}
public String getThumbnailUrl() {
return thumbnailUrl;
}
public Integer getDuration() {
return duration;
}
public String getContentId() {
return contentId;
}
public Date getReportDate() {
return reportDate;
}
public Integer getCount() {
return count;
}
}
Class Y
public class Y {
private final String thumbnailUrl;
private final Integer duration;
private final String contentId;
private final Map<Date, Integer> contentList;
public Y(X x, Map<Date, Integer> contentList) {
this.thumbnailUrl = x.getThumbnailUrl();
this.duration = x.getDuration();
this.contentId = x.getContentId();
this.contentList = contentList;
}
public String getThumbnailUrl() {
return thumbnailUrl;
}
public Integer getDuration() {
return duration;
}
public String getContentId() {
return contentId;
}
public Map<Date, Integer> getContentList() {
return contentList;
}
}
This is what I am currently getting from class X
[
{
"thumbnailUrl": "a",
"duration": 12,
"contentId": "CNT10",
"reportDate": "2020-01-20",
"count": 3
},
{
"thumbnailUrl": "a",
"duration": 12,
"contentId": "CNT10",
"reportDate": "2020-01-21",
"count": 5
},
{
"thumbnailUrl": "a",
"duration": 12,
"contentId": "CNT10",
"reportDate": "2020-01-22",
"count": 3
},
{
"thumbnailUrl": "a",
"duration": 12,
"contentId": "CNT10",
"reportDate": "2020-01-23",
"count": 4
}
]
I got the above json in postman after using this code and returning the final list.
List<X> x;
List<Y> y;
x = StreamSupport.stream(reportRepository
.reportWithRoll(a, b, c).spliterator(), false)
.map(report -> new X(report, report.getStartCount()))
.collect(Collectors.toList());
I want this to transformed into content of Class Y as below. How can I achieve this with streams in Java?
[
{
"list": [
{
"2020-01-20": 3,
"2020-01-21": 5,
"2020-01-22": 3,
"2020-01-23": 4
}
],
"thumbnailUrl": "a",
"duration": 12,
"contentId": "CNT10"
}
]
I tried this to get the above json format, but ended up getting duplicate data, for single contentId and error for multiple contentId
y = x.stream().map(
rep -> {
Map<Date, Integer> contentList = x.stream().collect(
Collectors.toMap(X::getReportDate, X::getCount)
);
Y yy = new Y(rep, contentList);
return yy;
}
).distinct().collect(Collectors.toList());
I am combining the common date and count : key value pair into a single "list" for each unique "contentId" (each unique "contentId" will have its own "thumbnailUrl" and "duration" specific to it, so when I am referring only "contentId" as unique, it will include "thumbnailUrl" and "duration", only the date and count will be multiple for each of these).
Upvotes: 2
Views: 5955
Reputation: 19
Why complicate? Use Map Struts... You can convert one object in another in compile time. This is a really good Library and it's tested and used in Spring
Upvotes: 0
Reputation: 1152
I have found a solution using stream itself, thanks to one stack overflow answer.
y = x.stream()
.map(X::getContentId)
.distinct()
.map(
contentId -> {
return reportModifier(reportViews, contentId);
}
).collect(Collectors.toList());
private Y reportModifier(List<x> x, String contentId) {
Map<Date, Integer> contentList = x.stream()
.filter(a -> a.getContentId().equals(contentId))
.collect(
Collectors.toMap(X::getReportDate, X::getCount)
);
X rv = x.stream()
.filter(a -> a.getContentId().equals(contentId))
.findFirst()
.get();
Y yy = new Y(rv, contentList);
return yy;
}
I got the following json with this
[
{
"thumbnailUrl": "a",
"duration": 12,
"contentId": "CNT10",
"contentList": {
"2020-01-21T18:30:00.000+0000": 3,
"2020-01-20T18:30:00.000+0000": 3,
"2020-01-22T18:30:00.000+0000": 3,
"2020-01-19T18:30:00.000+0000": 3
}
},
{
"thumbnailUrl": "b",
"duration": 12,
"contentId": "CNT12",
"contentList": {
"2020-01-19T18:30:00.000+0000": 3
}
},
{
"thumbnailUrl": "c",
"duration": 12,
"contentId": "CNT11",
"contentList": {
"2020-01-21T18:30:00.000+0000": 3,
"2020-01-20T18:30:00.000+0000": 3,
"2020-01-19T18:30:00.000+0000": 3
}
}
]
Upvotes: 1
Reputation: 32036
Considering the contentId
would bring along the same values for thumbnailUrl
and duration
for any X
in the input, you can do that in two steps :
Map<String, X> contentIdLookUp = x.stream()
.collect(Collectors.toMap(X::getContentId, Function.identity()));
List<Y> y = x.stream()
.collect(Collectors.groupingBy(X::getContentId,
Collectors.toMap(X::getReportDate, X::getCount))))
.entrySet().stream()
.map(e -> new Y(contentIdLookUp.get(e.getKey()), e.getValue()))
.collect(Collectors.toList());
Upvotes: 1
Reputation: 1460
If I understand the question, you're just collecting all your X
objects into a Y
object?
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collector.html
public class YCollector<X, Y, Y> {
public Supplier<Y> supplier() {
return () -> new Y();
}
public BiConsumer<X, Y> accumulator {
return (x, y) -> {
y.list.add(x.thumbnailUrl);
y.duration += x.duration;
...
return y;
}
}
public BinaryOperator<Y> combiner {
return (y1, y2) -> {
return y1.merge(y2);
}
}
public Function<Y, Y> finisher() {
return (y) -> y;
}
}
You're essentially just: - Creating a new Y - Rolling all your X values into Y - Returning the final Y
Edit
I just noticed you wanted the final result to be a List<Y>
instead of Y
. You can use the same thing, you'll just need to update the methods accordingly to handle that. Probably use
class YCollector<X, Map<String,Y>, List<Y>> {
// Update methods to combine into Map<ContentId, Y> and then finalize with map values.
}
Upvotes: 0