How to get cumulative amount total from list of Map - java

In one of my case I get the input like below which has a list inside with list of Maps.
List<Map<String, String>> actAllSavAccDetLists = test1Page.getAllSavingsAccountsDetails();
// returns like this
[
{Savings=Account ****2623, Current Balance=$22000.00, Annual Rate=7.77%, Transfer=Make a Transfer, Ellipses=...},
{Savings=Account ****5678, Current Balance=$11000.00, Annual Rate=2.22%, Transfer=Make a Transfer, Ellipses=...}
]
Now I need to find the total balance for the user, i.e.; adding up all the current balance from the Map inside a list.
Say in this case, adding $22000.00 + $11000.00 to give the result as $33000.00 in a total_bal variable.

You can easily use java stream map->reduce to make that:
Double totalBalance = actAllSavAccDetLists.stream()
.map(e -> e.get("Current Balance").substring(1))
.map(e -> new BigDecimal(e))
.reduce(BigDecimal.ZERO, BigDecimal::add);

The java-compatible way to do it is not to have this map at all. Java is nominally typed, and really likes its types. a Map<String, String> is not an appropriate data type here.
First, make a class that represents a savings account.
Next, instead of having a List<Map<String, String>>, have a List<SavingsAccount>.
Finally, sum up the balances.
Making a class
Looks like it would be something along the lines of:
#lombok.Value
public class SavingsAccount {
String accountId;
int balance; // in cents
double rate; // might need to be BigDecimal
}
You'll need to festoon it up to become a proper java class (the fields need to be final and private, getters and setters nee dto be there, a constructor, equals, toString, etcetera). I'm using lombok here (disclaimer: I'm one of the developers), but you can also use a java16 record, or use your IDE to generate all this stuff.
Converting that mess into instances of SavingsAccount
Converting a map that contains for example a mapping Transfer = Make a Transfer into an instance of this rather strongly suggests your input is coming from some bizarre source. You'll know better than we do how to convert it. You can now localize all the various required conversions and open questions into a single place. For example, what should happen if, say, map.get("CurrentBalance") doesn't exist, or returns "€10000.00"?
This boils down to "How do I convert the string "$22000.00" into the integer 2200000", or "How do I convert "7.77%" into a double", which is not difficult, and an unrelated question; if you're having trouble with it, I'm sure it's been answered a million times on SO already so you'll find it swiftly with a web search.
Summing it up
That's trivial:
List<SavingsAccount> accounts = ...;
int sum = accounts.stream().mapToInt(SavingsAccount::getBalance).sum();
This streams all the accounts, extracts just the balance from each, and then sums the entire stream into a single number.
I don't want to make that class
Well, it's a bit silly to do things in ways no sane java programmer would ever do. If you're trying to learn, you'll be learning the wrong ways of work. If you're trying to deliver freelance work, you'll get negative reviews. If you're "in a hurry", taking shortcuts now will just cost you triple later on. You do want to make that class.
If you insist on being stubborn, the same techniques can be used, just, with the order all jumbled up. You can stick the code that extracts the balance in that mapToInt call:
.mapToInt(s -> extractBalanceFromThisBizarroMap(s))
and then just write static int extractBalanceFromThisBizarroMap(Map<String, String> s) yourself.
But don't do that.

Related

How to Group elements inside the list using drools

I am new to Drools and i am struggling to find the solution for below problem:
I have a list of Account class:
class Account {
private String name;
private float amount;
}
I want to group the list of Account using name in drools.
For example:
Account a = new Account("Science", 100);
Account b = new Account("Science", 200);
List<Account> list = new ArrayList<String>();
list.add(a);
list.add(b);
Now I need a drool rule that should group the elements inside the list using name and provide the list that will have "Science, 300".
Please suggest.
As Ironluca points out in the comments, you are using the wrong tool for the job. The correct solution would be to use Java streams; even a simple for-loop would solve your problem.
Of course, since you can use a hammer on a screw, you can do this with Drools. I will include an example rule and an explanation, though it will be much less efficient than a stream-based solution (or even a for-loop.)
I will assume this model, with appropriate getters, setters, and an all-args constructor:
class Account {
String name;
int quantity;
}
Then this rule will populate an 'output' list with the "grouped" accounts. I've declared this 'output' list as a global, which isn't best-practice but none of this is recommended anyway since it's not the appropriate tool for the job.
global List $output;
rule "Consolidate duplicates"
when
$inputs: List()
Account( $name: name ) from $inputs
$duplicates: List()
from collect( Account( name == $name ) from $inputs )
$total: Number()
from accumulate( Account( $value: quantity ) from $duplicates, sum( $value ) )
then
$output.add(new Account($name, $total));
end
The first part of the rule identifies the subset of Account instances in the input list which share the same name. I use collect because this is a simple subset of the original input. The next step is to use accumulate to sum up the quantities from those duplicates. Then we add a new instances to the output list on the rule's RHS with the consolidated quantity and the shared name. Note that the rule works the same for Accounts which have unique names; there is no constraint on length for $duplicates, and the accumulation would just be the one identified quantity.
Modifying $inputs in-place is also do-able, but tricky, since you have to worry about concurrent modification exceptions. The much cleaner implementation is to use
Of course this is way more complicated than it has to be. Java 8 streams provide a groupingBy collector which allows you to trivially create a map by a property within the object:
Map<String, List<Account>> accountsByName = accounts.stream()
.collect(groupingBy(Account::getName));
You could then easily transform this by iterating over the valueset.
This other question goes into detail for other 'summation of duplicates in a stream' scenarios: Java 8 stream sum entries for duplicate keys

In java, How to link between original RDD and the RDD I get after transformations

I have an RDD of custom objects, let's say Person. then I use several narrow (although could be wide) transformations on this RDD, each time I get a new RDD. finally I get an RDD with a different type, let's say an Integer.
Now I want to know in some way what Integer linked to each Person, and to print it like this:
person a -> 3
person b -> 1
person c -> 7
I tried:
JavaPairRDD resultRDD = myRDD.mapToPair(rec -> new Tuple2(rec, new SomeFunction.call(rec)));
this code works for me because I can get each tuple and print it. but I'm not sure if it is a good way to implement this when there are many transformations (is it?)
thought to use another option: transformedRDD.parent(number,evidence) and in that way get the original RDD and then some how to identify the reference between the Person and the Integer.
Note: evidence is scala.reflect.ClassTag<U> and I am not familiar with scala so I don't really understand what to write there
Any help will be appreciated!
I would simply carry a key with me all the way.this way its easier to avoid miss identification as each object comes with its id every time. in other words:
persons
.map(p => (id, p))
.map( (id, p) => (id, transformation1(p)) )
.map( (id, p) => (id, transformation2(p)) )
....
I think there is no right or wrong answer to this question. There could be a better answer though.
You are on the right track to first think about making rdd to PairRDD. However as you said that there are many transformations on to the initial RDD structute, it gets complicated quickly.
Sorry about the bad drawing.. Anyway probably for multidependency, it is not very clear what to put at key field of PairRDD.
I am not sure if this is the case for you but I think if the relationship is not one to one, there could be many Persons which produces one Integer.
If you are using reduce operation to the Integer before you interpret the dependency information, you need to concern that an Integer may not have only one ancestor.
Anyway, I think the best way to solve this problem is that you add a unique identifier ArrayList field in the RDD. Instead of making a PairRDD, which adds unnecessary structure, just think about this field as a graph that denotes ancestry of current RDD's field.
For example, Persons object would have a field named "dependency" which is length 0 arraylist because it has no ancestor.
After that, let's say that you have a transformation to Double for some reason. Then the resulting RDD contains a field named "dependency" which has length 1 which denotes the unique identifier field of Person object.
Lastly we have transformation to Integer. Again we have a RDD with field named "dependency" which is length 2(because we had two ancestors for this one integer) that denotes unique identifier of Person object and unique identifier of Double object.
I think my explanation is bit lengthy and verbose but I hope you get the meaning..
Lastly if you are doing reduce operation between the RDDs, you have to consider if you really have one to one case. Because one Integer may not have come from the one Person object, if you want to discover full lineage of this Integer, you got to add all dependency information to the arraylist. Also when you decipher this "dependency" arraylist, you have to keep in mind that the length could be arbitrary for the list if the relationship is not one to one and if you are using reduce between the RDDs.
The best solution I thought was this one but I think there could be simpler answer to this question. If you find out one let me know!
After some experiments I decided to use the following solution:
JavaRDD<Person> persons = sc.parallelize(personList);
JavaRDD<Person,SomeType> trans1 = persons.mapToPair(p -> new Tuple2<Person,SomeType>(p, someFunction.call(p)));
JavaRDD<Person,OtherType> trans2 = trans1.mapToPair(tuple -> new Tuple2<Person,OtherType>(tuple._1(), otherFunction.call(tuple._2())));
you could continue as much as you want, and you always have a reference to the Person object. It can be done in more concise way with .mapToPair without declaring other RDDs but for me it is more clear like this.

Handling object data in Java

Suppose I have a file like this:
Account1 +200
Account2 Holder:John
Account3 -200
Account2 -100
and so on.
I want to be able to query for example "Account1" for money. The Account names can be arbitrary in the text file. How should I go about doing this in Java? I know this sounds suspiciously derpish but for the life of me I can't figure out a way that seems right.
The obvious idea would be to make an ArrayList with objects of type "Account". However then every time you wanted to check an account, you'd have to go through every single item of the ArrayList and carry out getName() to check if it's equal to it, which seems very labour intensive for simply bringing up an object. Is there any way you could somehow convert between string/data and object handles since Java is an interpretive language?
An obvious solution is to use a HashMap<String, Account> map.
check this: https://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html
Then to get Account1 you do: map.get("Account1");

4 Key Value HashMap? Array? Best Approach?

I've got loads of the following to implement.
validateParameter(field_name, field_type, field_validationMessage, visibleBoolean);
Instead of having 50-60 of these in a row, is there some form of nested hashmap/4d array I can use to build it up and loop through them?
Whats the best approach for doing something like that?
Thanks!
EDIT: Was 4 items.
What you could do is create a new Class that holds three values. (The type, the boolean, and name, or the fourth value (you didn't list it)). Then, when creating the HashMap, all you have to do is call the method to get your three values. It may seem like more work, but all you would have to do is create a simple loop to go through all of the values you need. Since I don't know exactly what it is that you're trying to do, all I can do is provide an example of what I'm trying to do. Hope it applies to your problem.
Anyways, creating the Class to hold the three(or four) values you need.
For example,
Class Fields{
String field_name;
Integer field_type;
Boolean validationMessageVisible;
Fields(String name, Integer type, Boolean mv) {
// this.field_name = name;
this.field_type = type;
this.validationMessageVisible = mv;
}
Then put them in a HashMap somewhat like this:
HashMap map = new HashMap<String, Triple>();
map.put(LOCAL STRING FOR NAME OF FIELD, new Field(new Integer(YOUR INTEGER),new Boolean(YOUR BOOLEAN)));
NOTE: This is only going to work as long as these three or four values can all be stored together. For example if you need all of the values to be stored separately for whatever reason it may be, then this won't work. Only if they can be grouped together without it affecting the function of the program, that this will work.
This was a quick brainstorm. Not sure if it will work, but think along these lines and I believe it should work out for you.
You may have to make a few edits, but this should get you in the right direction
P.S. Sorry for it being so wordy, just tried to get as many details out as possible.
The other answer is close but you don't need a key in this case.
Just define a class to contain your three fields. Create a List or array of that class. Loop over the list or array calling the method for each combination.
The approach I'd use is to create a POJO (or some POJOs) to store the values as attributes and validate attribute by attribute.
Since many times you're going to have the same validation per attribute type (e.g. dates and numbers can be validated by range, strings can be validated to ensure they´re not null or empty, etc), you could just iterate on these attributes using reflection (or even better, using annotations).
If you need to validate on the POJO level, you can still reuse these attribute-level validators via composition, while you add more specific validations are you´re going up in the abstraction level (going up means basic attributes -> pojos -> pojos that contain other pojos -> etc).
Passing several basic types as parameters of the same method is not good because the parameters themselves don't tell much and you can easily exchange two parameters of the same type by accident in the method call.

Java HashMap indexed on 2 keys

I want to create a HashMap in java for users with preferences. This would be easy to do in a database, but unfortunately I can't use a database. What I need is a way to find a user by name in the HashMap, and to find all the users with a certain interest (e.g. golf). If I delete a user, then all their interests should be deleted.
Anyone know a nice way to make this data structure?
I would suggest you create your own data structure for holding the information. Inside that class you could have two HashMaps storing the relevant information. Then write your own methods to insert and delete a user.
This way you have control over the insert/delete-operations while being able to query each attribute separately.
Do you know you really need to have a second index. You may find that a search of every user is fast enough, unless you have millions of users.
The following example takes 51 micro-second to scan 1,000 users. It takes 557 micro-seconds to scan 10,000 users.
I wouldn't suggest optimising the collection until your know whether it would make a difference.
import java.util.*;
import java.io.*;
public class TestExecutor {
public static void main(String[] args) throws IOException {
Map<String, User> users = new LinkedHashMap<String, User>();
generateUsers(users, 1000, 0.1);
// warmup.
int count = 10000;
for(int i=0;i< count;i++)
getAllUsersWithInterest(users, Interest.Golf);
long start = System.nanoTime();
for(int i=0;i< count;i++)
getAllUsersWithInterest(users, Interest.Golf);
long time = System.nanoTime() - start;
System.out.printf("Average search time %,d micro-seconds%n", time/ count/1000);
}
private static Set<User> getAllUsersWithInterest(Map<String, User> users, Interest golf) {
Set<User> ret = new LinkedHashSet<User>();
for (User user : users.values()) {
if (user.interests.contains(golf))
ret.add(user);
}
return ret;
}
private static void generateUsers(Map<String, User> users, int count, double interestedInGolf) {
Random rand = new Random();
while(users.size() < count) {
String name = Long.toString(rand.nextLong(), 36);
EnumSet<Interest> interests = rand.nextFloat() < interestedInGolf
? EnumSet.of(Interest.Golf) : EnumSet.noneOf(Interest.class);
users.put(name, new User(name, interests));
}
}
static class User {
private final String name;
private final Set<Interest> interests;
User(String name, Set<Interest> interests) {
this.name = name;
this.interests = interests;
}
}
enum Interest {
Golf
}
}
Simplest solution is to use a Commons Collection MultiKeyMap even if it is lacking generics.
...Check this thread too genericized-commons-collection
it seems like you could use something like a bi-directional map to implement something like this. check out http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?com/google/common/collect/BiMap.html for some doco.
although it doesnt give you exactly what you need in the question, its half the way there.
This may be overkill for your needs, but I don't know how complex and speed sensitive your needs are, so I'll throw it out there...
Have you considered looking at an in-memory (or even local disk based like SQLite) database to handle your data. Doing so would allow you to store your data in a way that allows much more power in how you search/index your data, without many of the costs of writing your own code.
Just put the users in an ArrayList, and walk over it till you found the one(s) you need. Give each user a set of interests. Once you get enough users that it takes too long, sort them.
Once that takes too long, take a look at the distribution of interests. If you have a low number of different ones, store them in a bitmap. If you have a limited set of combinations of interests, store them separately and give a user one of these.
Start simple, computers are fast. But hide the implementation, so you can change it.
[hmm, getting negative votes for this]. Look at the question: you'll need a lot of users before this code is as slow as a database. (on current hardware, at least a few hundred thousand)
I would implement the following
HashMap which include the user as key and the value could be any object which includs the userpreferences. The user preferences would include a list of interest for example.
And an additional HashMap with an interest as key and a list of users who are interested in this.
When you delet a user, you can get all the interest he has and delete the user name from the interest HashMap list.
When the interest HashMap list is empty you can delet the interest from the HashMap.
Take care, when 2 or more users have the same interest. You can not delet the interest when only one user is deleted.
The downside is, that you will have redundant informations.
You could use 2 HashMaps. But searching only trough preferences could be complicated.
HashMap <String,Hashmap> users;
//save data
//create new user
HashMap <String,String> prefs;
//save prefs
prefs.put(pref1,value1);
prefs.put(pref2,value2);
//save user
users.put(user1,prefs);
//get data
String x = users.get(user1).get(pref1);
Maybe you don't need this solution anymore, but many people still have same problems.

Categories

Resources