I have class let's say CheeseMojo having various fields as follow:
public class CheeseMojo {
private String recipies = "Recipies";
private int age;
private String name;
private int submissionId;
//getter/setter
}
Now I want to sort it based on submissionId using Collectors.groupingBy function, then below code snippet will do the job
Map<Integer,List<CheeseMojo>> map = new HashMap<>();
map = cheeseMojos.stream().collect(Collectors.groupingBy(CheeseMojo::getSubmissionId));
And the output will be something like below:
{1=[CheeseMojo#111111],2=[CheeseMojo#222222]}
But I want my output something like below:
{"Recipies1"=[CheeseMojo#111111],"Recipies2"=[CheeseMojo#222222]}
Please help
Why don't you just map the key either before collecting (lets say by creating an instance of a wrapper class that has
public class CheeseMojoWrapper {
private final String id;
private final CheeseMojo wrapped;
public CheeseMojoWrapper(CheeseMojo toWrap) {
id = "Recipe" + toWrap.getId();
wrapped = toWrap;
}
}
or using a stream on the entrySet() of your resulting map...
Related
I have a Pojo class with all numeric fields
public class Pojo {
private long field1;
private long field2;
private long field3;
private long field4;
private long field5;
private double field6;
private double field7;
private double field8;
private double field9;
}
And there is a list of Pojo, I want map this list to one Pojo object which will contain in its field the of pojos foe, list. I mean someething like this :
public static void main(String[] args) {
List<Pojo> pojoList = getPogoList();
Pojo pojoInSum = reduceAllFields(pojoList);
}
What is the simplest way to reduce all fields of pojos from list without reflection?
You could use the Stream#reduce method:
public void reducePojoList() {
PojoReduce pojoReduce = new PojoReduce();
List<Pojo> pojoList = Arrays.asList(
new Pojo(3L, 4.0),
new Pojo(6L, 1.1));
Optional<Pojo> reducedPojo = pojoList.stream().reduce(this::combine);
reducedPojo.ifPresent(System.out::println);
}
private Pojo combine(Pojo pojo1, Pojo pojo2) {
return new Pojo(
pojo1.getLongField() + pojo2.getLongField(),
pojo1.getDoubleField() + pojo2.getDoubleField()
);
}
You would have to update the combine method for every field you add though. You'd also be creating a lot of new objects.
The simplest way is to write a method in that pojo. Because if you are modeling a thing in a class you should expose behavior and not data.
But I doubt that is what you are looking for so you might want to look at reflection.
Basically you retrieve all the fields of a class, get the values for the instance and then sum them in a loop or stream.
I have POJO List<TravelRequestDTO> and I want to group and create filtered List<TravelRequestDTO> if leavingFrom,goingTo,onwarDate,returnDate are same add passenger to same object
Example :
Passanger, onWard, return, leavingFrom, goingTo
A, 1-2-20, 3-2-20, BLR, PUNE
B, 1-2-20 , 3-2-20, BLR, PUNE
final List<TravelRequestDTO> should contain :
Passanger, onWard, return, leavingFrom, goingTo
A,B 1-2-20 3-2-20 BLR PUNE
public class TravelRequestDTO {
private List<Pax> passangers;
private String leavingFrom;
private String goingTo;
private String onwarDate;
private String onwardTime;
private String returnDate;
private String returnTime;
private SegmentTypeEnum segmentType;
private TravelModeEnum travelMode;
private String purposeOfVisit;
}
public class Pax{
private String name;
private String age;
private String mobile;
}
If you need older java version, than you can do like this:
Map<Object, List<TravelRequestDTO>> hashMap = new HashMap<Object, List<TravelRequestDTO>>();
for (TravelRequestDTO value: initList) {
List<Object> key = Arrays.asList(value.getOnWard(),value.getReturn(),value.getLeavingFrom(),value.getGoingTo());
if (!hashMap.containsKey(key)) {
List<TravelRequestDTO> list = new ArrayList<TravelRequestDTO>();
list.add(value);
hashMap.put(key, list);
} else {
hashMap.get(key).add(value);
}
}
Check this question for other solution.
It is only half way to what you want. After that you have to extract final result from this map. Or you can do it in one step:
Map<Object, TravelRequestDTO> hashMap = new HashMap<Object, TravelRequestDTO>();
for (TravelRequestDTO value: initList) {
List<Object> key = Arrays.asList(value.getOnWard(),value.getReturn(),value.getLeavingFrom(),value.getGoingTo());
if (!hashMap.containsKey(key)) {
TravelRequestDTO item = value; // pass first value or copy it to new
hashMap.put(key, item);
} else {
hashMap.get(key).getPassangers().addAll(value.getPassangers());
}
}
List<TravelRequestDTO> result = new ArrayList<>(hashMap.values());
you can use the below code to get a List of TravelRequestDTO objects based on properties.
Function<TravelRequestDTO, List<Object>> compositeKey = travelRecord ->
Arrays.<Object>asList(travelRecord.getOnWard(),travelRecord.getReturn(),travelRecord.getLeavingFrom(),travelRecord.getGoingTo());
Map<Object, List<TravelRequestDTO>> map =
people.collect(Collectors.groupingBy(compositeKey, Collectors.toList()));
I often find myself in a situation where I need to create a Map of objects from a Set or List.
The key is usually some String or Enum or the like, and the value is some new object with data lumped together.
The usual way of doing this, for my part, is by first creating the Map<String, SomeKeyValueObject> and then iterating over the Set or List I get in and mutate my newly created map.
Like the following example:
class Example {
Map<String, GroupedDataObject> groupData(final List<SomeData> list){
final Map<String, GroupedDataObject> map = new HashMap<>();
for(final SomeData data : list){
final String key = data.valueToGroupBy();
map.put(key, GroupedDataObject.of(map.get(key), data.displayName(), data.data()));
}
return map;
}
}
class SomeData {
private final String valueToGroupBy;
private final Object data;
private final String displayName;
public SomeData(final String valueToGroupBy, final String displayName, final Object data) {
this.valueToGroupBy = valueToGroupBy;
this.data = data;
this.displayName = displayName;
}
public String valueToGroupBy() {
return valueToGroupBy;
}
public Object data() {
return data;
}
public String displayName() {
return displayName;
}
}
class GroupedDataObject{
private final String key;
private final List<Object> datas;
private GroupedDataObject(final String key, final List<Object> list) {
this.key = key;
this.datas = list;
}
public static GroupedDataObject of(final GroupedDataObject groupedDataObject, final String key, final Object data) {
final List<Object> list = new ArrayList<>();
if(groupedDataObject != null){
list.addAll(groupedDataObject.datas());
}
list.add(data);
return new GroupedDataObject(key, list);
}
public String key() {
return key;
}
public List<Object> datas() {
return datas;
}
}
This feels very unclean. We create a map, and then mutate it over and over.
I've taken a liking to java 8s use of Streams and creating non-mutating data structures (or rather, you don't see the mutation). So is there a way to turn this grouping of data into something that uses a declarative approach rather than the imperative way?
I tried to implement the suggestion in https://stackoverflow.com/a/34453814/3478016 but I seem to be stumbling. Using the approach in the answer (the suggestion of using Collectors.groupingBy and Collectors.mapping) I'm able to get the data sorted into a map. But I can't group the "datas" into one and the same object.
Is there some way to do it in a declarative way, or am I stuck with the imperative?
You can use Collectors.toMap with a merge function instead of Collectors.groupingBy.
Map<String, GroupedDataObject> map =
list.stream()
.collect(Collectors.toMap(SomeData::valueToGroupBy,
d -> {
List<Object> l = new ArrayList<>();
l.add(d.data());
return new GroupedDataObject(d.valueToGroupBy(), l);
},
(g1,g2) -> {
g1.datas().addAll(g2.datas());
return g1;
}));
The GroupedDataObject constructor must be made accessible in order for this to work.
If you avoid the GroupedDataObject and simply want a map with a key and a list you can use Collectors.groupingBy that you have been looking into.
Collectors.groupingBy will allow you to do this:
List<SomeObject> list = getSomeList();
Map<SomeKey, List<SomeObject>> = list.stream().collect(Collectors.groupingBy(SomeObject::getKeyMethod));
This will require SomeKey to have proper implementations of equals and hashValue
Sometimes streams are not the way to go. I believe this is one of those times.
A little refactoring using merge() gives you:
Map<String, MyTuple> groupData(final List<SomeData> list) {
Map<String, MyTuple> map = new HashMap<>();
list.forEach(d -> map.merge(d.valueToGroupBy(), new MyTuple(data.displayName(), data.data()),
(a, b) -> {a.addAll(b.getDatas()); return a;});
Assuming a reasonable class to hold your stuff:
class MyTuple {
String displayName;
List<Object> datas = new ArrayList<>();
// getters plus constructor that takes 1 data and adds it to list
}
I have this situation and I'm not sure why is this happening, maybe you can help me on this.
I have a class MyClass with this structure. (with getters & setters)
public class MyClass {
private String id;
private String title;
private boolean anyBoolean;
And I added some static attributes such us
public class MyClass {
public static final Group CONS_1 = new Group("cons_1","Cons 1", false);
public static final Group CONS_2 = new Group("cons_2","Cons 2", true);
private String id;
private String title;
private boolean anyBoolean;
public MyClass(String id, String title, boolean anyBoolean) {
this.id = id;
this.title = title;
this.anyBoolean = anyBoolean;
}
Just FYI, I didn't use enums because I have to serialize all the attributes and with the enums it was serializing only the name.
Later I realize it will good to have a way to expose these constants by ID such as
public static Map<String, MyClass> myMap;
I tried a basic approach like
static {
myMap = new HashMap<>();
myMap.put(CONS_1.getId(), CONS_1);
}
and it worked, obviously.
But I wondered if I could do this with Stream, like
myMap = Stream.of(CONS_1, CONS_2).collect(Collectors.groupingBy(MyClass::getId));
But is not working, because getId() is non static.
So my question is why the second way is not working since both approaches looks equivalent?
Thanks!
Finally I make it work. The proper way to create that map is
myMap = Stream.of(CONS_1, CONS_2).collect(Collectors.toMap(MyClass::getId, Function.identity()));
I don't understand why the compiler complained about the static method though.
I am trying to deserialize following xml:
<scenario name="test responses">
<cmd name="query1">
<return>success_200.xml</return>
<return>error_500.xml</return>
</cmd>
<cmd name="query2">
<return>success_200.xml</return>
</cmd>
</scenario>
into this class
#Root(name="scenario")
public class TestScenario {
#ElementMap(entry="cmd", key="name", attribute=true, inline=true)
private Map<String,StepsList> scenario;
#Attribute(required = false)
private String name = "";
public static class StepsList {
#ElementList(name="return")
private List<String> steps = new ArrayList<String>();
public List<String> getSteps() {
return steps;
}
}
}
But get a org.simpleframework.xml.core.ValueRequiredException: Unable to satisfy #org.simpleframework.xml.ElementList
How it can be done?
Try this:
#ElementList(required = false, inline = true, name="return")
private List<String> steps = new ArrayList<String>();
So, after couple hours of research, I created a working solution.
Strange enough, but to create a map of arrays you need to use #ElementList decoration with special SimpleFramework utility class Dictionary. Objects inserted into that dictionary must implement Entry interface and can contain any parsing rules. In my case they contain List<String> corresponding to inner <return> tags.
You can read about utility classes in the tutorial: http://simple.sourceforge.net/download/stream/doc/tutorial/tutorial.php#util
#Root(name="scenario")
public class TestScenario {
#ElementList(inline=true)
private Dictionary<StepsList> scenario;
#Attribute(required = false)
private String name = "";
public Dictionary<StepsList> getScenario() {
return scenario;
}
#Root(name="cmd")
public static class StepsList implements Entry {
#Attribute
private String name;
#ElementList(inline=true, entry="return")
private List<String> steps;
#Override
public String getName() {
return name;
}
public List<String> getSteps() {
return steps;
}
}
}
Dictionary is a class implementing java.util.Set, and you can use it like this:
TestScenario test = loadScenario("test.xml");
String step1 = test.getScenario().get("query1").getSteps().get(0);
// step1 is now "success_200.xml"
String step2 = test.getScenario().get("query1").getSteps().get(1);
// step2 is now "error_500.xml"