简体   繁体   中英

convert one class object into another using stream

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).

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());

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
    }
}
]

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.
}

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM