Merge data together based on an ID Java streams - java

I currently have a set of data I pull back from an API in my java app. The data returned is along the lines of the following:
{
"id": 1,
"receiptId": "123456",
"selections": [
{
"name": "Apple",
"price": "£1"
}
]
},
{
"id": 2,
"receiptId": "678910",
"selections": [
{
"name": "Pear",
"price": "£0.5"
}
]
},
{
"id": 3,
"receiptId": "123456",
"selections": [
{
"name": "Banana",
"price:": "£2.00"
}
]
}
As you can see, two of the receiptId's are the same, I would like to merge any duplicate receiptId's data together to become one block. I.e:
{
"id": 1,
"receiptId": "123456",
"selections": [
{
"name": "Apple",
"price": "£1"
},
{
"name": "Banana",
"price": "£2.00"
}
]
},
{
"id": 2,
"receiptId": "678910",
"selections": [
{
"name": "Pear",
"price": "£0.5"
}
]
},
Currently I am just streaming the data into a map by doing the following:
List<String> data = data.getData()
.stream()
.map(this::dataToReadable)
.collect(Collectors.toList());
dataToReadable does the following:
private String dataToReadable(List data) {
return new DataBuilder().fromData(data)
.buildData();
}
fromData does the following:
public DataBuilder fromData(List data) {
this.withId(data.getId())
this.withSelections(data.getSelections())
this.withReceiptId(data.getReceiptId())
return this;
}

See if this works.
data.getData()
.stream()
.collect(Collectors.groupingBy(Data::getRecieptId))
.entrySet()
.stream()
.map(item -> dataToReadable(item.getValue()))
.collect(Collectors.toList());

Related

find the record of OutterObject where InnerObject id is x and date is most recent

I am trying to get the id of the OutterObject where the InnerObject id is a specific value and the date is the most recent of all InnerObject of all OutterObject.
I'm trying to achieve that with streams.
Searching for id "ab", it should return "def"
here is a json example of the structre.
{
"OutterObject": [
{
"id": "abc",
"InnerObject": [
{
"id": "ab",
"date": "1"
},
{
"id": "de",
"date": "2"
},
{
"id": "ab",
"date": "3"
}
]
},
{
"id": "def",
"InnerObject": [
{
"id": "ab",
"date": "9"
},
{
"id": "de",
"date": "3"
},
{
"id": "ab",
"date": "1"
}
]
}
]
}
Use flatMap to gather all objects in the same array :
OutterObject.stream().flatMap(outer -> outer.getInnerObject().stream());
Use max() and Comparator to get the higher value
Combine both to get something like :
Optional<Date> maxDate = OutterObject.stream().flatMap(outer -> outer.getInnerObject().stream()).max(Comparator.comparing(InnerObject::getDate));
This is solved with :
Optional<SimpleImmutableEntry<String, InnerObject>> mostRecentOutterObjectId= MasterObject.getOutter().stream()
.flatMap(outter-> outter.getInner().stream()
.map(inner-> new SimpleImmutableEntry<String, InnerObject>(outter.getId(), inner)))
.filter(outterMap-> StringUtils.equals(outterMap.getValue().getId(), "ab")))
.max((innerA,innerB) -> innerA.getValue().getDate().compareTo(innerB.getValue().getDate()));
I am open to improvement if you see some.

How to merge two lists after comparing using java streams API

I have two lists of same type:
Boxes1 and Boxes2:
"boxes1": [{
"boxId": "ABC",
"ele": [{
"eleId": "8040",
"chars": [{
"no": "123",
"qty": 2
}
]
}
]
}, {
"boxId": "XYZ",
"ele": [{
"eleId": "1212",
"chars": [{
"no": "456",
"qty": 3
}
]
}
]
}
]
And
"boxes2": [{
"boxId": "ABC",
"ele": [{
"eleId": "8040",
"chars": [{
"no": "123",
"qty": 6
}
]
}, {
"eleId": "4560",
"chars": [{
"no": "012",
"qty": 3
}
]
}
]
}, {
"boxId": "PQR",
"ele": [{
"eleId": "1111",
"chars": [{
"no": "456",
"qty": 8
}
]
}
]
}
]
I want check if any boxId from boxes1 matches with a boxId from boxes2, then I want to add the chars of the a common element eleId of box1 into box2 so that the result looks like this after combining:
"box2": [{
"boxId": "ABC",
"ele": [{
"eleId": "8040",
"chars": [{
"no": "123",
"qty": 6
},
{
"no": "123",
"qty": 2
}
]
},.....................
and also if there is any boxId from Boxes1 that is not present in Boxes2 I want to add that box inside the Boxes2. so finally it should look like :
"box2": [{
"boxId": "ABC",
"ele": [{
"eleId": "8040",
"chars": [{
"no": "123",
"qty": 6
}, {
"no": "123",
"qty": 2
}
]
}, {
"eleId": "4560",
"chars": [{
"no": "012",
"qty": 3
}
]
}
]
}, {
"boxId": "PQR",
"ele": [{
"eleId": "1111",
"chars": [{
"no": "456",
"qty": 8
}
]
}
]
}, {
"boxId": "XYZ",
"ele": [{
"eleId": "1212",
"chars": [{
"no": "456",
"qty": 3
}
]
}
]
}
]
I am new to Stream so tried using the old way but it has issue:
boxes2.forEach(box2 -> {
boxes1.forEach(box1 -> {
if (box1.getBoxId().equals(box2.getBoxId())) {
box2.getEle().forEach(ele1 -> {
box1.getEle().forEach(ele2 -> {
if (ele1.getEleId().equals(ele2.getEleId())) {
ele1.chars().addAll(ele2.chars());
}
});
});
} else {
boxes2.add(box1);
}
});
});
Using Streams won't help much in your case. I'd suggest to go for POIC (plain old imperative code).
Pseudo code alorithm:
for ( box1 of boxes1 ) {
var box1Id = box.boxId;
if ( boxes2.containsBoxId(box1Id) ) {
var box2 = boxes2.getBoxById(boxId);
for ( ele1 of box1.ele ) {
var ele1Id = ele1.eleId;
if ( box2.containsEleId(ele1Id) ) {
var ele2 = box2.getEleById(ele1Id);
ele2.addToChars(ele1.chars);
}
}
} else {
// if boxes2 doesn't contain box1Id
boxes2.addBox(box1);
}
}
The only thing I would consider for Streams is the "find matching ele and add chars" part. But that will get slow for huge collections.
var box1Id = ...
var box1ele = ...
boxes2.stream()
.filter(b -> b.boxId.equals(box1Id))
.flatMap(b -> b.ele.stream())
.filter(e -> e.eleId.equals(box1ele))
.forEach(e -> e.addChars(box1ele.chars)
You could of course do the box1 iteration with Streams, too. But that will lead to some really ugly and unreadable code. (EDIT: Just like your example code: Not nice.)
What you could do instead: Put the relevant data in a Map for faster and easier access without all those loops.
An other solution is like this:
List<Box> boxResult = Stream.concat(boxes1.stream(), boxes2.stream())
.collect(toMap(Box::getBoxId,
v-> new ArrayList<>(v.getEle()),
YOUR_CLASS::mergeEle))
.entrySet().stream()
.map(entry -> new Box(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
and mergeEle method:
private static List<Ele> mergeEle(List<Ele> l1, List<Ele> l2) {
return Stream.concat(l1.stream(), l2.stream())
.collect(toMap(Ele::getEleId,
v -> new ArrayList<>(v.getChars()),
YOUR_CLASS::mergeChar))
.entrySet().stream()
.map(e -> new Ele(e.getKey(), e.getValue()))
.collect(Collectors.toList());
}
and mergeChar:
private List<Char> mergeChar(List<Char> c1, List<Char> c2) {
c1.addAll(c2);
return c1;
}

How to Extract the Json Value from Json String using Java?

This is my json structure and I want to get the values of hierarchy_name from each json object in an Array , can someone help me with that using Java..
{
"hits": {
"total": {
"value": 218
},
"max_score": null,
"hits": [
{
"_index": "planlytx",
"_source": {
"hierarchy_name": "PRODUCT"
},
"fields": {
"attribute_name.keyword": [
"PRODUCT"
]
}
},
{
"_index": "planlytx",
"_source": {
"hierarchy_name": "PRODUCT"
},
"fields": {
"attribute_name.keyword": [
"PRODUCT-ALL"
]
}
}
]
}
}
Using org.json, you can do:
JSONArray hitsArray = new JSONObject(jsonStr).getJSONObject("hits").getJSONArray("hits");
List<String> keywords = IntStream.range(0, hitsArray.length())
.mapToObj(hitsArray::getJSONObject)
.map(json -> json.getJSONObject("fields")
.getJSONArray("attribute_name.keyword")
.toList())
.flatMap(objects -> objects.stream().map(Object::toString))
.collect(Collectors.toList());
For "hierarchy_name":
JSONArray hitsArray = new JSONObject(jsonStr).getJSONObject("hits").getJSONArray("hits");
List<String> keywords = IntStream.range(0, hitsArray.length())
.mapToObj(hitsArray::getJSONObject)
.map(json -> json.getJSONObject("_source").getString("hierarchy_name"))
.collect(Collectors.toList());
Output:
[PRODUCT, PRODUCT-ALL]

Filter in list by responseEentity in Java

I need to filter a list with a response
ResponseEntity<Gruoup> listaGrupo = monstarListaGrupo(login, token);
List<Diretorio> diretorios = lista.stream()
.filter(x-> x.getGrupoAd() == listaGrupo.getBody().getGrupos())
I tried to do it that way but he doesn't let it because it says that one type is list and another is String
Apartment filter of a RespondeEntity in an age list for exam
RespondeEntity:
{
"name": "barry",
"age": 20
},
{
"name": "allan",
"age": 17
},
{
"name": "julie",
"age": 20
},
{
"name": "jord",
"age": 19
}
List:
{
"name": "jhenny",
"age": 20,
"color": "red"
},
{
"name": "barry",
"age": 20,
"color": "black"
},
{
"name": "julie",
"age": 20,
"color": "white"
}
Result final List
{
"name": "barry",
"age": 20,
"color": "black"
},
{
"name": "julie",
"age": 20,
"color": "white"
}
You are trying to compare a String with a List<Grupo>, but it seems you want to filter based on the response entity containing the grupoAd of the directio.
First collect the Strings from the List into a Set, then use contains() for the filter, somethingh like this:
ResponseEntity<Gruoup> listaGrupo = monstarListaGrupo(login, token);
Set<String> names = listaGrupo.getBody().getGrupos().stream().collect(toSet());
List<Diretorio> diretorios = lista.stream()
.filter(x -> names.contains(x.getGrupoAd()))
...

Recursively sort JSON arrays and object properties in Java

The goal is to start with a JSON string, parse it, and then recursively process every object property or array element. If the value is an array, and every element of the array is an object that contains a "Name" property, sort the array by the object.Name. The recursion is important, because the real-world JSON I am going to have to apply this to has three levels of nested arrays that need to be sorted.
Once the arrays are sorted into a deterministic order, finally, it should re-serialize the whole thing with properties in alphabetical order.
The purpose of this is to ensure consistency of the JSON, so I can do string comparison between the expected and the actual, and have it not be thrown off if things are serialized in a different order, or the array elements come back in a non-deterministic order (which is currently the case).
In pseudo-code, it would look something like this:
process_array( arr ) {
if ( arr.every( e => e.has("Name") ) ) {
arr.sort( byName )
}
arr.forEach( process )
}
process_object( obj ) {
for each key in obj {
process( obj[key] )
}
process( it ) {
if ( isArray( it ) {
process_arr( it )
} else {
process_object( it )
}
}
standardize( json ) {
generic_obj = parse( json )
process( generic_obj )
return serialize_with_sorted_keys( generic_obj )
}
To make it concrete, given this input:
[
{
"Id": "1",
"Name": "foo",
"Children": [
{
"Name": "two",
"Value": 2
},
{
"Value": 1,
"Name": "one"
}
],
"Other": [ 1, 3, 2 ]
},
{
"Name": "bar",
"Id": "2",
"Children": [
{
"Name": "Banana",
"Value": 3
},
{
"Value": 4,
"Name": "Cherry"
},
{
"Apples": "are tasty",
"Name": "Apple",
"Value": 5
}
]
}
]
The expected output would be:
[ // array elements are sorted by .Name
{ // object properties are sorted by key
"Children": [ // sorted by .Name
{
"Apples": "are tasty",
"Name": "Apple",
"Value": 5
},
{
"Name": "Banana",
"Value": 3
},
{ // properties sorted by name
"Name": "Cherry",
"Value": 4
}
],
"Id": "2",
"Name": "bar"
},
{
"Children": [
{
"Name": "two",
"Value": 2
},
{
"Name": "one",
"Value": 1
}
],
"Id": "1",
"Name": "foo",
"Other": [ 1, 2, 3 ] // NOTE: order has been changed!
}
]
How do I do this in Java in a generic way?

Categories

Resources