Reduce a flux to mono using data from flux - java

I have this scenario. I have one paginated API which gives me the data for last 12 months. The response of the API is like:
public class PagedTransfersDto {
private List<Transfer> content;
private Page page;
#Getter
public static class Transfer {
private String id;
private Long transferId;
private Long transferRequestId;
private String status;
private BigDecimal accountReceivable;
private BigDecimal accountPayable;
private BigDecimal netReceivable;
private BigDecimal netPayable;
private String currency;
private Long transferDate;
}
#Getter
public static class Page {
private Integer size;
private Integer number;
private Integer totalElements;
private Integer totalPages;
}
}
Now I have to collect all the data and then calculate the sum of all the netReceivable and return as a Mono<CompanyIncome>. This pojo is like
public class CompanyIncome {
private BigDecimal inferredIncome = new BigDecimal(0);
}
To do this I have written something like:
CompanyIncome initialIncome = new CompanyIncome();
return myService.getTransfers(0, 50, fromDate, toDate)
.expand(pagedTransfersDto -> {
if (pagedTransfersDto.getPage().getNumber().equals(pagedTransfersDto.getPage().getTotalPages())) {
return Mono.empty();
}
return myService.getTransfers(pagedTransfersDto.getPage().getNumber() + 1, 50, fromDate, toDate);
})
.flatMap(pagedTransfersDto -> Flux.fromIterable(pagedTransfersDto.getContent()))
.reduce(initialIncome, ((companyIncome, transfer) -> {
companyIncome.setInferredIncome(companyIncome.getInferredIncome().add(transfer.getNetReceivable()));
return companyIncome;
}));
Now the catch is that it is possible that this data is only for 3 months in which case I have to extrapolate this to 12 months by multiplying by 4.
What I am thinking is to get the first item of transfers list and the last one and the see if the data is not for a whole year but cant think of a place where to perform this operation.
Since after reduce the transfers data is gone. Before that I cannot seem to find a way how to get this info and still reduce from transfers flux
I am a little new to reactive way and cant seem to find a way to do this. Any help will be greatly appreciated. Thanks

For that purpose, the best solution is to store the necessary "metadata" in the reduced object. You already have a CompanyIncome object, so maybe that is a good place? Otherwise I'd introduce either a Tuple2 or some intermediate business object (eg. CompanyIncomeAggregator) into which to store both the aggregated income and the information that you need to decide at the end if further processing is necessary.
Then in a map step, you'd read that information, act on it and either return the computed income as is or modified according to your criterion.
Important note: Using variables external to the reactive chain is a code smell, as it introduces leaky shared state: if two subscriptions are made to the same Mono, they'll work on the same CompanyIncome object. You can remediate here by using reduceWith, which takes a Supplier for the initial value: reduceWith(CompanyIncome::new, ...).

Related

Performance of Map vs Optional in Java [duplicate]

This question already has answers here:
Java Performance: Map vs List
(4 answers)
Closed 1 year ago.
I have a doubt about the performance difference between these two things, get an object directly from a hashmap with the key vs get it from an Optional from an ArrayList. I will use these to save big amounts of data.
Note: the example below is only to show what I mean; I don't use static except in utils or specific things, I say this to prevent comments about static.
public class Main {
private static final List<User> users = Arrays.asList(new User(UUID.randomUUID()), new User(UUID.randomUUID()), new User(UUID.randomUUID()));
public static Optional<User> getUserByUUID(final UUID uuid){
return users.stream().filter(user -> user.getUuid().equals(uuid)).findFirst();
}
#RequiredArgsConstructor
#Getter#Setter
private static class User{
private final UUID uuid;
private int points;
private int gems;
}
}
vs
public class Main {
private static final Map<UUID, User> users = new HashMap<UUID, User>(){{
put(UUID.randomUUID(), new User());
put(UUID.randomUUID(), new User());
}};
public static User getUserByUUID(final UUID uuid){
if(users.containsKey(uuid))
return users.get(uuid);
return null;
}
#RequiredArgsConstructor
#Getter#Setter
private static class User{
private int points;
private int gems;
}
}
My point is, if one is better than the another one in terms of performance, is it insignificant?
Map#get will always be more performant than creating a Stream from a List and looking for a specific entry.
Map#get will give you a time complexity of O(1) basically
List#stream instead will give you a time complexity of O(n) plus extra space complexity: creation of a Stream and creation of an Optional
That said, if you have big amounts of data loaded in memory, this might lead to performance problems / OutOfMemoryErrors
It would be interesting to dig further in the problem and see if there isn't another way to handle this specific problem without loading big amounts of data directly in memory of the JVM

Partitioning a Stream

I am working with a Stream of Person data and I've run into a partitioning issue related to the data.
I have a Stream of data which I'll represent below in a table:
ID Name Ticket IsEmployee
1 A Y Y
2 B
3 C Y
4 D
I am trying to return a List that is sorted by:
whether or not they're an Employee
if they have any Tickets
then by Name
I've looked into Collections.groupBy and Collections.partitioningBy, but so far haven't been able to come up withe the correct result.
My expectations are to return a list in the following order (by ID):
1 [name="A",Ticket="**[100,101]**", IsEmployee="**Y**"],
3 [name="C",Ticket="**[200,201]**", IsEmployee=""],
2 [name="**B**",Ticket="", IsEmployee=""],
4 [name="D",Ticket="", IsEmployee=""]
Any thoughts on how this might be accomplished without having to totally break apart the Stream?
Below is what my Person looks like:
public class Person {
private long id;
private String name;
private List<Ticket> tickets;
private String employeeType; // This is just a 'Y'/'N' value. This property has morphed into something else but I'm stuck using it.
public long getId(){
return id;
}
public String getName(){
return name;
}
public List<Ticket> getTickets(){
return tickets;
}
public String getEmployeeType(){
return id;
}
// Setters are the exact same as getters, meaning I have no transient methods
}
Since you are saying “I am trying to return a List that is sorted by…”, it’s not clear why you started looking for grouping or partitioning, instead of aiming exactly at, what your problem description is about, get a List and sort it by your criteria:
// collect into a mutable List, if it isn’t a mutable List in the first place
List<Person> list=stream.collect(Collectors.toCollection(ArrayList::new));
// sort by your specified criterie (note: false < true)
list.sort(Comparator.comparing((Person p) -> !p.getEmployeeType().equals("y"))
.thenComparing(p -> p.getTickets().isEmpty())
.thenComparing(Person::getName) );
You can also specify the operation as part of the Stream operation, e.g.
List<Person> list=stream
.sorted(
Comparator.comparing((Person p) -> !p.getEmployeeType().equals("y"))
.thenComparing(p -> p.getTickets().isEmpty())
.thenComparing(Person::getName) )
.collect(Collectors.toList());
but there is no technical benefit. The Stream implementation has to collect the entire contents into a temporary buffer internally, to sort it before it is collected (i.e. copied) into the resulting List. The in-place sorting of ArrayList may get away with lesser or even without data copying (not that it matters for four elements, but in general).

Map with custom object as key to DataFrame in Apache Spark

I'm having trouble with creating a DataFrame from an RDD.
To start off, I'm using Spark to create the data I'm using (via simulations on the workers) and in return I get Report objects.
These Report object consist of two HashMaps where the keys are near identical between the maps and custom made and the values are Integer / Double. Worth noting is that I currently need these keys and maps to efficiently add and update the values during the simulations, so changing this to a "flat" object may lose a lot of efficiency.
public class Key implements Serializable, Comparable<Key> {
private final States states;
private final String event;
private final double age;
...
}
And the States are
public class States implements Serializable, Comparable<States> {
private String stateOne;
private String stateTwo;
...
}
The states used to be Enums, but as it turns out, DataFrame doesn't like that. (The Strings are still set from Enums to ensure the values are correct.)
The problem is that I want to convert these maps to DataFrames so that I can use SQL etc to manipulate/filter the data.
I am able to create DataFrames by creating a Bean like so
public class Event implements Serializable {
private String stateOne;
private String stateTwo;
private String event;
private Double age;
private Integer value;
...
}
with getters and setters, but is there a way that I can just use Tuple2 (or something similar) to create my DataFrame? Which could even give me a nice structure for the db?
I have tried using Tuple2 like this
JavaRDD<Report> reports = dataSet.map(new SimulationFunction(REPLICATIONS_PER_WORKER)).cache();
JavaRDD<Tuple2<Key, Integer>> events = reports.flatMap(new FlatMapFunction<Report, Tuple2<Key, Integer>>() {
#Override
public Iterable<Tuple2<Key, Integer>> call(Report t) throws Exception {
List<Tuple2<Key, Integer>> list = new ArrayList<>(t.getEvents().size());
for(Entry<Key, Integer> entry : t.getEvents().entrySet()) {
list.add(new Tuple2<>(entry.getKey(), entry.getValue()));
}
return list;
}
});
DataFrame schemaEvents = sqlContext.createDataFrame(events, ????);
But I don't know what to put where the question marks are.
Hopefully I've made myself clear enough and that you'll be able to shed some light on this. Thank you in advance!
As zero323 says, it's not possible to do what I'm trying to do. I'll just stick with the beans from now on.

Using Realm.io to store money values

I'm starting to play with Realm.io in an Android app that I'm writing. In one of my data objects, I'm required to store a currency value. Previously I had stored the value internally as a BigDecimal value and then converted that too and from a double value when moving in and out of the database.
I have always been told that it is a bad idea to store currency values in a double because of the way that they are handled.
Unfortunately, Realm.io doesn't support the storage and retrieval of BigDecimal objects.
Is the best solution to write my own currency class that extends RealmObject and keeps that as a member variable of by data object?
Emanuele from Realm here.
You are right, using floats or doubles for currency is a bad idea.
We don't support BigDecimal for now, and before we do we will have to see how that plays in relation to all other language bindings since we want realm files to be compatible across all the supported platforms.
Christian's idea is good, but I see the conversion to and from String to be a bit slow. If you don't need the arbitrary precision property of BigDecimal you could use long and multiply/divide by the factor your required precision calls for. This would also save a lot of space in terms of the size of the Realm file since integer values are bit packed.
That could work, but would probably be suboptimal if do calculations on your current BigDecimal objects.
You could also use the #Ignore annotation to provide a wrapper method for your custom objects like this:
public class Money extends RealmObject {
private String dbValue;
#Ignore private BigDecimal value;
public String getDbValue() {
return dbValue;
}
public void setDbValue(String dbValue) {
this.dbValue = dbValue;
}
public BigDecimal getValue() {
return new BigDecimal(getDbValue());
}
public void setValue(BigDecimal value) {
setDbValue(value.toString());
}
}
It is not perfect as you need to expose the *dbValue() methods, but it should work.
I would also suggest going to https://github.com/realm/realm-java/issues and make a feature request for this as BigDecimal is probably one of those java classes used by so many that it could warrant native Realm support, just like Date has.
What I do is store it as long
I have defined in my application a constant like so:
public static final BigDecimal MONEY_PRECISION = new BigDecimal(1000);
and when I need to store a big decimal it goes like this:
public class MoneyClass extends RealmObject {
long _price = 0;
public void set_price(BigDecimal price) {
this._price = price.longValue() * App.MONEY_PRECISION.longValue();
}
public BigDecimal get_price() {
return new BigDecimal(_price).divide(App.MONEY_PRECISION, 0, 0);
}
}
In theory this should be faster than saving it on strings , but I haven't really looked at the realm code much
My solution:
Define Interface:
public interface RealmConvert {
void convertToDB();
void convertToObj();
}
Define Entity:
#Ignore
private BigDecimal balance;
private String balanceStr;
#Override public void convertToDB() {
if (getBalance() != null) {
setBalanceStr(getBalance().toString());
}
}
#Override public void convertToObj() {
if (getBalanceStr() != null) {
setBalance(new BigDecimal(getBalanceStr()));
}
}
Before you copyToRealm:call method convertToDB
When you need to use the entity: call method convert obj
It's not an elegant solution, but it works.
Christian Melchior's answer doesn't work in my app.

Applying design patterns to set and get matrix values of currency

I am using a web service to get currency rates for 4 different currencies.
What I am doing so far is to get these rates and store then in a 4x4 matrix in a way that any value can be easily retrieved without having to use the web service everytime.
What I want to know is what is the best approach, using design patterns (and which one would be more appropriate) to set and get the values of this matrix.
I am currently just using something like this:
public void setPoundToEuro(float value) {
currencyMatrix[0][1] = value;
}
public float getPoundToEuro() {
return currencyMatrix[0][1];
}
What I was hoping is to have something more abstract to whichever class needs to use this service and get these values. Something like another class calling a method just sending two Strings and the same method would return any currency rates, depending on the Strings received. In this case it would be "pound" and "euro".
I hope to have made myself clear, but if not, please let me know.
I have not seen much questions like this here, so I hope this is not a problem, I am trying to discuss and find the best approach for my problem.
I have already seen this design patterns for currency conversion? and it did help clarify somethings for me, but the situation is slightly different, so I thought it was reasonable to ask a new question.
Not exactly rocket science, still needs additional checks when converting, if the rates have not yet been defined and has room for improvements, but I believe it's a bit more object oriented and you get the picture.
If you were to follow Marcelo's suggestion with dedicated converters, the currencyCache could be a Map<String, Converter> and the convert method something like currencyCache.get(from+to).calculate(amount)
public class CurrencyConverter {
static Map<String, Map<String, Double>> currencyCache = new HashMap<>();
static {
for (ConversionDefinition definition : ConversionDefinition.values()) {
Map<String, Double> rates = currencyCache.get(definition.from);
if (rates == null) {
rates = new HashMap<>();
currencyCache.put(definition.from, rates);
}
rates.put(definition.to, definition.rate);
}
}
public static Double convert(String from, String to, Double amount) {
return currencyCache.get(from).get(to) * amount;
}
public enum ConversionDefinition {
EURO_TO_USD("euro", "usd", 10d),
USD_TO_EUR("usd", "euro", 1 / 10d);
private final String from;
private final String to;
private final Double rate;
ConversionDefinition(String from, String to, Double rate) {
this.from = from;
this.to = to;
this.rate = rate;
}
}
}
I apprectiate a lot all of the suggestions given. Thanks to them I've come up with a very simple solution myself, using HashMap.
I have created a class that is basically the following:
private Map currencyMap;
public CurrencyData() {
currencyMap = new HashMap();
}
public void setCurrencyValue(String key, String value) {
currencyMap.put(key, value);
}
public String getCurrencyValue(String key) {
return currencyMap.get(key).toString();
}
And on my Main class, as the program initializes, I simply use the web service to fill in the hash map by calling the method setCurrencyValue.
If anyone spots any flaws on this current approach, please let me know, I am still open to suggestions.

Categories

Resources