0

I'm looking to convert a List of Maps to another List of Maps which is grouped.

So I have the below list of maps -

List<Map<String, Object>

[{
"accId":"1",
"accName":"TestAcc1",
"accNumber":"101",
"currency":"USD",
"amount":"1000"
},
{
"accId":"1",
"accName":"TestAcc1",
"accNumber":"101",
"currency":"GBP",
"amount":"800"
},
{
"accId":"1",
"accName":"TestAcc1",
"accNumber":"101",
"currency":"EUR",
"amount":"900"
},
{
"accId":"2",
"accName":"TestAcc2",
"accNumber":"102",
"currency":"USD",
"amount":"9000"
},
{
"accId":"2",
"accName":"TestAcc2",
"accNumber":"102",
"currency":"GBP",
"amount":"8000"
},
{
"accId":"2",
"accName":"TestAcc2",
"accNumber":"102",
"currency":"EUR",
"amount":"9500"
}
]

Data table for reference:

accId   accName     accNumber   currency    amount
1       TestAcc1    101         USD         1000
1       TestAcc1    101         GBP         800
1       TestAcc1    101         EUR         900
2       TestAcc2    102         USD         9000
2       TestAcc2    102         GBP         8000
2       TestAcc2    102         EUR         9500

I'd like to group the currency + amount for the accounts to something like below:

List<Map<String, Object>

[{
"accId":"1",
"accName":"TestAcc1",
"accNumber":"101",
"currencies":
{
"USD":"1000",
"GBP":"800",
"EUR","900"
}
},
{
"accId":"2",
"accName":"TestAcc2",
"accNumber":"102",
"currencies":
{
"USD":"9000",
"GBP":"8000",
"EUR","9500"
}
}
]

I was wondering how to go about doing this. Can somebody please help me out here?

  • Let us know if you solved your problem. If you did, please [accept](https://stackoverflow.com/help/someone-answers) the most helpful answer, or add your own answer explaining how you solved it. – Tomasz Linkowski Jul 28 '18 at 11:40

2 Answers2

2

It's best to make use of composition here - consider creating these kinds of objects:

  • AccountInfo with account ID, account name and account number (you would need it to have value semantics, i.e. equals and hashCode implemented - see e.g. @EqualsAndHashCode)
  • AccountBalance with account currency and amount
  • AccountRecord with an AccountInfo and an AccountBalance
  • Account with an AccountInfo and a list of AccountBalances

Now what you want to do simplifies to mapping a stream of AccountRecords into a stream of Accounts. This can be achieved by:

  1. Mapping your objects into AccountRecords.
  2. Grouping by AccountInfo.
  3. Constructing an Account from the AccountInfo and a list of AccountRecords.

Example:

Stream<AccountRecord> accountStream = Stream.of(); // your data here
List<Account> accountList = accountStream
        .collect(Collectors.groupingBy(AccountRecord::getAccountInfo))
        .entrySet().stream()
        .map(entry -> new Account(
                entry.getKey(),
                entry.getValue().stream()
                        .map(AccountRecord::getAccountBalance)
                        .collect(Collectors.toList())
        ))
        .collect(Collectors.toList());
Tomasz Linkowski
  • 4,386
  • 23
  • 38
  • 1
    +1 for recommending the usage of concrete classes instead of working with maps. I think according to the question, the `Account` should hold a map of `AccountBalance` (or just amounts) per currency though, instead of a simple list. – Didier L Jul 17 '18 at 14:35
1

General description of method:

You can make a separate currency map: Map currency map = new HashMap<>(); Then you will need an if statement to group your currencies based upon your accId since the currency-map holds all of your currencies with a common accId. You then need to find all your unique Ids, group them and add a currencyMap to them.

Code Example:

Map<String,Map<String,String>> comparisonMap = new HashMap<>();
Map<String,String> currencyMap = new HashMap<>();
for(Map<String,String> myMap:myListOfMaps){
    //if we have seen this id before or the currency map is empty
    //then put in the newest entry in the currencymap and set the currentId
    if (currencyMap.keySet().size() = 0 || myMap.get("aacId").equals(currentId)){
        currencyMap.put(myMap.get("currency"),myMap.get("amount"))}
        String currentId = myMap.get("aacId")
    //if we see a different ID then, record the old id and old currency map, set the 
    //currentId to the new Id, create a new currencyMap and add in this entry's data 
    //to the new currencyMap
    else {
        comparisonMap.put(currentId,currencyMap);
        currentId = myMap.get("aacId");
        Map<String,String> currencyMap = new HashMap<>();
        currencyMap.put(myMap.get("currency"),myMap.get("amount"));  
    }
}

new Map<String,Object> Accountmap = new HashMap<>();
new ArrayList<Map<String,Object>> finalList = new ArrayList<>();
}
//loop through your original list and seperate out individual unique accounts.
for(Map<String,String> myMap:myListOfMaps){
     //if the accountmap is empty, fill in the first account you see
     if(Accountmap.get("aacID") == null && 
     Accountmap.get("aacName") == null && 
     Accountmap.get("aacNumber") == null){
         Accountmap.put("aacID",myMap.get("aacID"));
         Accountmap.put("aacName",myMap.get("aacName"));
         Accountmap.put("aacNumber",myMap.get("aacNumber"));
     }
     //if the account info you see next is different than what you have, add the 
     //account info you have now to the final list and create a new Accountmap
     //to hold the newest data we are seeing now.
     else if(!(Accountmap.get("aacID").equals(myMap.get("aacID")) && 
     Accountmap.get("aacName").equals(myMap.get("aacName")) && 
     Accountmap.get("aacNumber").equals(myMap.get("aacNumber")))))
     {
         finalList.add(Accountmap);
         new Map<String,Object> Accountmap = new HashMap<>();
         Accountmap.put("aacID",myMap.get("aacID"));
         Accountmap.put("aacName",myMap.get("aacName"));
         Accountmap.put("aacNumber",myMap.get("aacNumber"));
     }
 }

finally once you have all your unique accounts in your finalList you can then loop through it and compare their unique Ids to each entry in the comparisonMap so you can put in entries (currencyMaps) into those unique Id maps (accountMaps).

 for(HashMap<String,Object> accountMap:finalList){
     //put in the key "currency" and the value as a currencyMap.
     //the currencyMaps are gotten by matching the Ids from the accountMap to the Id
     //in the comparisonMap 
     //conveniently the ID maps to its currencyMap
     accountMap.put("currencies",comparisonMap.get(accountMap.get("aacId")))
 }

Notes about output

At the end your finalList would contain the list of accountMaps as the desired output for this question. I don't make any guarantees on the run-ability of this code but It shows a general method to achieving what you need.

this solution assumes your accounts are listed like they are in the question. It won't work as expected if the same account info is repeated AND split between accounts down the list. (i. e.) nothing like:

  1. Account 1 information
  2. Account 2 information
  3. Account 1 information repeated.

the last entry would cause duplicate entries in your finalList

Redacted
  • 613
  • 6
  • 23