Skip to content
This repository was archived by the owner on May 18, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.idea/
docs/
docs/
2 changes: 2 additions & 0 deletions input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
EXPENSE u4 1200 4 u1 u2 u3 u4 PERCENT 20 20 20 20 20
SHOW u1
33 changes: 33 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.arpit</groupId>
<artifactId>mock-machine-coding-2</artifactId>
<version>1.0-SNAPSHOT</version>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
</dependencies>

</project>
17 changes: 17 additions & 0 deletions src/main/java/com/SplitWiseRunner.java
Original file line number Diff line number Diff line change
@@ -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());
}
}
32 changes: 32 additions & 0 deletions src/main/java/com/core/InputProcessor.java
Original file line number Diff line number Diff line change
@@ -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<String> {

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]));
}
}
}
10 changes: 10 additions & 0 deletions src/main/java/com/dao/ExpenseDao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.dao;

import com.models.Transaction;

public interface ExpenseDao {

void addTransaction(Transaction transaction);

String showUserBalance(String userId);
}
49 changes: 49 additions & 0 deletions src/main/java/com/dao/impl/InMemoryExpenseDao.java
Original file line number Diff line number Diff line change
@@ -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<String, UserBalance> userBalanceData = new HashMap<>();

private InMemoryExpenseDao() { }

public static ExpenseDao getInstance() {
return IN_MEMORY_EXPENSE_DAO;
}

@Override
public void addTransaction(Transaction transaction) {
List<Borrow> 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<Borrow> debitList = userBalance.getAllPayTo();
List<Borrow> 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();
}
}
32 changes: 32 additions & 0 deletions src/main/java/com/factory/SplitStrategyFactory.java
Original file line number Diff line number Diff line change
@@ -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");
}
}
20 changes: 20 additions & 0 deletions src/main/java/com/models/Borrow.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
20 changes: 20 additions & 0 deletions src/main/java/com/models/Transaction.java
Original file line number Diff line number Diff line change
@@ -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<Borrow> borrowList;

public String getSpentBy() {
return spentBy;
}
}
66 changes: 66 additions & 0 deletions src/main/java/com/models/UserBalance.java
Original file line number Diff line number Diff line change
@@ -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<String, Double> payTo;
private Map<String, Double> 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<Borrow> getAllReceiveFrom() {
List<Borrow> allReceiveFrom = new ArrayList<>();
receiveFrom.forEach((key, value) -> {
allReceiveFrom.add(Borrow.builder().lender(userId).borrower(key).amount(value).build());
});
return allReceiveFrom;
}

public List<Borrow> getAllPayTo() {
List<Borrow> allPayTo = new ArrayList<>();
payTo.forEach((key, value) -> {
allPayTo.add(Borrow.builder().lender(key).borrower(userId).amount(value).build());
});
return allPayTo;
}
}
8 changes: 8 additions & 0 deletions src/main/java/com/split/SplitStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.split;

import com.models.Transaction;

public interface SplitStrategy {

Transaction split(String inputLine);
}
15 changes: 15 additions & 0 deletions src/main/java/com/split/impl/EqualSplitStrategy.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
16 changes: 16 additions & 0 deletions src/main/java/com/split/impl/ExactSplitStrategy.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
56 changes: 56 additions & 0 deletions src/main/java/com/split/impl/PercentSplitStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.split.impl;

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");
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should have parsed input format outside the core design. You could have parsed and created an expense object and passed it inside the split method.

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<Borrow> 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");
}
}
}