From 9e9f8d321169ae537de58fa06cc666738b2b5686 Mon Sep 17 00:00:00 2001 From: arpit728 Date: Sun, 22 Sep 2019 00:45:20 +0530 Subject: [PATCH 1/3] solved the problem --- .gitignore | 3 + input.txt | 2 + pom.xml | 33 ++++++++++ src/main/java/com/SplitWiseRunner.java | 17 +++++ src/main/java/com/core/InputProcessor.java | 32 +++++++++ src/main/java/com/dao/ExpenseDao.java | 10 +++ .../java/com/dao/impl/InMemoryExpenseDao.java | 49 ++++++++++++++ .../com/factory/SplitStrategyFactory.java | 32 +++++++++ src/main/java/com/models/Borrow.java | 20 ++++++ src/main/java/com/models/Transaction.java | 20 ++++++ src/main/java/com/models/UserBalance.java | 66 +++++++++++++++++++ src/main/java/com/split/SplitStrategy.java | 8 +++ .../com/split/impl/EqualSplitStrategy.java | 15 +++++ .../com/split/impl/ExactSplitStrategy.java | 16 +++++ .../com/split/impl/PercentSplitStrategy.java | 58 ++++++++++++++++ 15 files changed, 381 insertions(+) create mode 100644 .gitignore create mode 100644 input.txt create mode 100644 pom.xml create mode 100644 src/main/java/com/SplitWiseRunner.java create mode 100644 src/main/java/com/core/InputProcessor.java create mode 100644 src/main/java/com/dao/ExpenseDao.java create mode 100644 src/main/java/com/dao/impl/InMemoryExpenseDao.java create mode 100644 src/main/java/com/factory/SplitStrategyFactory.java create mode 100644 src/main/java/com/models/Borrow.java create mode 100644 src/main/java/com/models/Transaction.java create mode 100644 src/main/java/com/models/UserBalance.java create mode 100644 src/main/java/com/split/SplitStrategy.java create mode 100644 src/main/java/com/split/impl/EqualSplitStrategy.java create mode 100644 src/main/java/com/split/impl/ExactSplitStrategy.java create mode 100644 src/main/java/com/split/impl/PercentSplitStrategy.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0da2c5c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/ +docs/ +docs/ diff --git a/input.txt b/input.txt new file mode 100644 index 0000000..47870ef --- /dev/null +++ b/input.txt @@ -0,0 +1,2 @@ +EXPENSE u4 1200 4 u1 u2 u3 u4 PERCENT 20 20 20 20 20 +SHOW u1 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..f664ae2 --- /dev/null +++ b/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + com.arpit + mock-machine-coding-2 + 1.0-SNAPSHOT + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 8 + 8 + + + + + + + + org.projectlombok + lombok + 1.18.10 + provided + + + + \ No newline at end of file diff --git a/src/main/java/com/SplitWiseRunner.java b/src/main/java/com/SplitWiseRunner.java new file mode 100644 index 0000000..c5d0565 --- /dev/null +++ b/src/main/java/com/SplitWiseRunner.java @@ -0,0 +1,17 @@ +package com; + +import com.core.InputProcessor; +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; + +/** + * Created by bugkiller on 21/09/19. + */ +public class SplitWiseRunner { + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new FileReader("input.txt")); + br.lines().forEach(new InputProcessor()); + } +} diff --git a/src/main/java/com/core/InputProcessor.java b/src/main/java/com/core/InputProcessor.java new file mode 100644 index 0000000..559eecf --- /dev/null +++ b/src/main/java/com/core/InputProcessor.java @@ -0,0 +1,32 @@ +package com.core; + +import com.dao.ExpenseDao; +import com.dao.impl.InMemoryExpenseDao; +import com.factory.SplitStrategyFactory; +import com.models.Transaction; +import com.split.SplitStrategy; +import java.util.function.Consumer; + +/** + * Created by bugkiller on 21/09/19. + */ +public class InputProcessor implements Consumer { + + private ExpenseDao expenseDao; + + public InputProcessor() { + expenseDao = InMemoryExpenseDao.getInstance(); + } + + @Override + public void accept(String inputLine) { + if (!inputLine.contains("SHOW")) { + SplitStrategy splitStrategy = SplitStrategyFactory.provide(inputLine); + Transaction transaction = splitStrategy.split(inputLine); + expenseDao.addTransaction(transaction); + } else { + String[] showForUser = inputLine.split("\\s"); + System.out.println(expenseDao.showUserBalance(showForUser[1])); + } + } +} diff --git a/src/main/java/com/dao/ExpenseDao.java b/src/main/java/com/dao/ExpenseDao.java new file mode 100644 index 0000000..dede7b1 --- /dev/null +++ b/src/main/java/com/dao/ExpenseDao.java @@ -0,0 +1,10 @@ +package com.dao; + +import com.models.Transaction; + +public interface ExpenseDao { + + void addTransaction(Transaction transaction); + + String showUserBalance(String userId); +} diff --git a/src/main/java/com/dao/impl/InMemoryExpenseDao.java b/src/main/java/com/dao/impl/InMemoryExpenseDao.java new file mode 100644 index 0000000..b5ff7e8 --- /dev/null +++ b/src/main/java/com/dao/impl/InMemoryExpenseDao.java @@ -0,0 +1,49 @@ +package com.dao.impl; + +import com.dao.ExpenseDao; +import com.models.Borrow; +import com.models.Transaction; +import com.models.UserBalance; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by bugkiller on 21/09/19. + */ +public class InMemoryExpenseDao implements ExpenseDao { + + private static final InMemoryExpenseDao IN_MEMORY_EXPENSE_DAO = new InMemoryExpenseDao(); + + private Map userBalanceData = new HashMap<>(); + + private InMemoryExpenseDao() { } + + public static ExpenseDao getInstance() { + return IN_MEMORY_EXPENSE_DAO; + } + + @Override + public void addTransaction(Transaction transaction) { + List borrowList = transaction.getBorrowList(); + for (Borrow borrow : borrowList) { + UserBalance lenderBalanceData = userBalanceData.getOrDefault(borrow.getLender(), new UserBalance(borrow.getLender())); + UserBalance borrowerBalanceData = userBalanceData.getOrDefault(borrow.getBorrower(), new UserBalance(borrow.getBorrower())); + lenderBalanceData.receiveFrom(borrow.getBorrower(), borrow.getAmount()); + borrowerBalanceData.payTo(borrow.getLender(), borrow.getAmount()); + userBalanceData.putIfAbsent(lenderBalanceData.getUserId(), lenderBalanceData); + userBalanceData.putIfAbsent(borrowerBalanceData.getUserId(), borrowerBalanceData); + } + } + + @Override + public String showUserBalance(String userId) { + UserBalance userBalance = userBalanceData.get(userId); + List debitList = userBalance.getAllPayTo(); + List creditList = userBalance.getAllReceiveFrom(); + StringBuilder sbr = new StringBuilder(); + debitList.forEach(borrow -> sbr.append(borrow.formattedOutput()).append("\n")); + creditList.forEach(borrow -> sbr.append(borrow.formattedOutput()).append("\n")); + return sbr.toString(); + } +} diff --git a/src/main/java/com/factory/SplitStrategyFactory.java b/src/main/java/com/factory/SplitStrategyFactory.java new file mode 100644 index 0000000..c03a278 --- /dev/null +++ b/src/main/java/com/factory/SplitStrategyFactory.java @@ -0,0 +1,32 @@ +package com.factory; + +import com.split.SplitStrategy; +import com.split.impl.EqualSplitStrategy; +import com.split.impl.ExactSplitStrategy; +import com.split.impl.PercentSplitStrategy; + +/** + * Created by bugkiller on 21/09/19. + */ +public class SplitStrategyFactory { + private static final SplitStrategy EQUAL_SPLIT_STRATEGY; + private static final SplitStrategy PERCENT_SPLIT_STRATEGY; + private static final SplitStrategy EXACT_SPLIT_STRATEGY; + + static { + EQUAL_SPLIT_STRATEGY = new EqualSplitStrategy(); + EXACT_SPLIT_STRATEGY = new ExactSplitStrategy(); + PERCENT_SPLIT_STRATEGY = new PercentSplitStrategy(); + } + + public static SplitStrategy provide(String inputLine) { + if (inputLine.contains("EXACT")) { + return EXACT_SPLIT_STRATEGY; + } else if (inputLine.contains("EQUAL")) { + return EQUAL_SPLIT_STRATEGY; + } else if (inputLine.contains("PERCENT")) { + return PERCENT_SPLIT_STRATEGY; + } + throw new IllegalArgumentException("Invalid Input"); + } +} diff --git a/src/main/java/com/models/Borrow.java b/src/main/java/com/models/Borrow.java new file mode 100644 index 0000000..8cac6bd --- /dev/null +++ b/src/main/java/com/models/Borrow.java @@ -0,0 +1,20 @@ +package com.models; + +import lombok.Builder; +import lombok.Getter; + +/** + * Created by bugkiller on 21/09/19. + */ +@Builder +@Getter +public class Borrow { + + private String borrower; + private String lender; + private double amount; + + public String formattedOutput() { + return String.format("%s Owes %s : %f", borrower, lender, amount); + } +} diff --git a/src/main/java/com/models/Transaction.java b/src/main/java/com/models/Transaction.java new file mode 100644 index 0000000..8333757 --- /dev/null +++ b/src/main/java/com/models/Transaction.java @@ -0,0 +1,20 @@ +package com.models; + +import java.util.List; +import lombok.Builder; +import lombok.Getter; + +/** + * Created by bugkiller on 21/09/19. + */ +@Builder +@Getter +public class Transaction { + + private String spentBy; + private List borrowList; + + public String getSpentBy() { + return spentBy; + } +} diff --git a/src/main/java/com/models/UserBalance.java b/src/main/java/com/models/UserBalance.java new file mode 100644 index 0000000..1d541e0 --- /dev/null +++ b/src/main/java/com/models/UserBalance.java @@ -0,0 +1,66 @@ +package com.models; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.Getter; + +/** + * Created by bugkiller on 21/09/19. + */ + +public class UserBalance { + + @Getter + private String userId; + private Map payTo; + private Map receiveFrom; + + public UserBalance(String userId) { + this.userId = userId; + payTo = new HashMap<>(); + receiveFrom = new HashMap<>(); + } + + public void receiveFrom(String userId, double amount) { + receiveFrom.merge(userId, amount, (oldValue, newValue) -> oldValue + amount); + settle(userId); + } + + public void payTo(String userId, double amount) { + payTo.merge(userId, amount, (oldValue, newValue) -> oldValue + amount); + settle(userId); + } + + private void settle(String externalUserId) { + Double payToUser = payTo.getOrDefault(externalUserId, (double) 0); + Double receiveFromUser = receiveFrom.getOrDefault(externalUserId, (double) 0); + if (payToUser > receiveFromUser) { + payTo.put(externalUserId, payToUser - receiveFromUser); + receiveFrom.remove(externalUserId); + } else if (payToUser < receiveFromUser) { + receiveFrom.put(externalUserId, receiveFromUser - payToUser); + payTo.remove(externalUserId); + } else { + receiveFrom.remove(externalUserId); + payTo.remove(externalUserId); + } + } + + public List getAllReceiveFrom() { + List allReceiveFrom = new ArrayList<>(); + payTo.forEach((key, value) -> { + allReceiveFrom.add(Borrow.builder().lender(userId).borrower(key).amount(value).build()); + }); + return allReceiveFrom; + } + + public List getAllPayTo() { + List allPayTo = new ArrayList<>(); + payTo.forEach((key, value) -> { + allPayTo.add(Borrow.builder().lender(key).borrower(userId).amount(value).build()); + }); + return allPayTo; + } +} diff --git a/src/main/java/com/split/SplitStrategy.java b/src/main/java/com/split/SplitStrategy.java new file mode 100644 index 0000000..94294f6 --- /dev/null +++ b/src/main/java/com/split/SplitStrategy.java @@ -0,0 +1,8 @@ +package com.split; + +import com.models.Transaction; + +public interface SplitStrategy { + + Transaction split(String inputLine); +} diff --git a/src/main/java/com/split/impl/EqualSplitStrategy.java b/src/main/java/com/split/impl/EqualSplitStrategy.java new file mode 100644 index 0000000..4e30188 --- /dev/null +++ b/src/main/java/com/split/impl/EqualSplitStrategy.java @@ -0,0 +1,15 @@ +package com.split.impl; + +import com.models.Transaction; +import com.split.SplitStrategy; + +/** + * Created by bugkiller on 21/09/19. + */ +public class EqualSplitStrategy implements SplitStrategy { + + @Override + public Transaction split(String inputLine) { + return null; + } +} diff --git a/src/main/java/com/split/impl/ExactSplitStrategy.java b/src/main/java/com/split/impl/ExactSplitStrategy.java new file mode 100644 index 0000000..1169ac7 --- /dev/null +++ b/src/main/java/com/split/impl/ExactSplitStrategy.java @@ -0,0 +1,16 @@ +package com.split.impl; + +import com.models.Transaction; +import com.split.SplitStrategy; + +/** + * Created by bugkiller on 21/09/19. + */ +public class ExactSplitStrategy implements SplitStrategy { + + @Override + public Transaction split(String inputLine) { + + return null; + } +} diff --git a/src/main/java/com/split/impl/PercentSplitStrategy.java b/src/main/java/com/split/impl/PercentSplitStrategy.java new file mode 100644 index 0000000..7057f09 --- /dev/null +++ b/src/main/java/com/split/impl/PercentSplitStrategy.java @@ -0,0 +1,58 @@ +package com.split.impl; + +import com.dao.ExpenseDao; +import com.dao.impl.InMemoryExpenseDao; +import com.models.Borrow; +import com.models.Transaction; +import com.split.SplitStrategy; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by bugkiller on 21/09/19. + */ +public class PercentSplitStrategy implements SplitStrategy { + + private static DecimalFormat decimalFormat = new DecimalFormat(".##"); + + public PercentSplitStrategy() { + decimalFormat.setRoundingMode(RoundingMode.UP); + } + + @Override + public Transaction split(String inputLine) { + String split[] = inputLine.split("PERCENT"); + String spenderUsersAndAmount[] = split[0].split("\\s"); + String shareList[] = split[1].split("\\s"); + validateShareList(shareList); + String spender = spenderUsersAndAmount[1]; + double amount = Double.parseDouble(spenderUsersAndAmount[2]); + List borrowList = new ArrayList<>(); + for (int i = 4, j = 1; i < spenderUsersAndAmount.length; i++) { + double share = Double.parseDouble(shareList[j]); + double borrowAmount = amount * (share / 100); + borrowList.add(Borrow.builder() + .lender(spender) + .borrower(spenderUsersAndAmount[i]) + .amount(borrowAmount) + .build()); + } + return Transaction.builder() + .borrowList(borrowList) + .spentBy(spender) + .build(); + } + + private void validateShareList(String[] shareList) { + double percentSum = 0; + for (int i = 1; i < shareList.length; i++) { + String percent = shareList[i]; + percentSum += Double.parseDouble(percent); + } + if (percentSum != 100) { + throw new IllegalArgumentException("Percent don't add up to 100"); + } + } +} From e1539a3081024a9eb7e97923261c7d45cb52afab Mon Sep 17 00:00:00 2001 From: arpit728 Date: Sun, 22 Sep 2019 07:55:03 +0530 Subject: [PATCH 2/3] solved the problem --- src/main/java/com/models/UserBalance.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/models/UserBalance.java b/src/main/java/com/models/UserBalance.java index 1d541e0..e13002d 100644 --- a/src/main/java/com/models/UserBalance.java +++ b/src/main/java/com/models/UserBalance.java @@ -50,7 +50,7 @@ private void settle(String externalUserId) { public List getAllReceiveFrom() { List allReceiveFrom = new ArrayList<>(); - payTo.forEach((key, value) -> { + receiveFrom.forEach((key, value) -> { allReceiveFrom.add(Borrow.builder().lender(userId).borrower(key).amount(value).build()); }); return allReceiveFrom; From 1cedadd2efb8cff9202c34209d8ae21904f3089a Mon Sep 17 00:00:00 2001 From: arpit728 Date: Sun, 22 Sep 2019 07:55:32 +0530 Subject: [PATCH 3/3] solved the problem --- src/main/java/com/split/impl/PercentSplitStrategy.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/com/split/impl/PercentSplitStrategy.java b/src/main/java/com/split/impl/PercentSplitStrategy.java index 7057f09..be84535 100644 --- a/src/main/java/com/split/impl/PercentSplitStrategy.java +++ b/src/main/java/com/split/impl/PercentSplitStrategy.java @@ -1,7 +1,5 @@ package com.split.impl; -import com.dao.ExpenseDao; -import com.dao.impl.InMemoryExpenseDao; import com.models.Borrow; import com.models.Transaction; import com.split.SplitStrategy;