Created
June 9, 2022 16:16
-
-
Save alexradzin/bfe219b653d1eea2cdd1f848c720059d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Implement code that calculates amounts of money in national currencies useing CSV file like | |
Base,Sum,Target | |
USD,100,ILS | |
USD,150,GBP | |
... | |
Use exchangeratesapi.io to retrieve currency rates. Print resutls to STDOUT. | |
(Interview question of Correlate) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.exercise; | |
import com.fasterxml.jackson.annotation.JsonCreator; | |
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | |
import com.fasterxml.jackson.annotation.JsonProperty; | |
import java.util.Map; | |
@JsonIgnoreProperties(ignoreUnknown = true) | |
public class ExchangeRate { | |
private final String base; | |
private final Map<String, Double> rates; | |
@JsonCreator | |
public ExchangeRate(@JsonProperty("base") String base, @JsonProperty("rates") Map<String, Double> rates) { | |
this.base = base; | |
this.rates = rates; | |
} | |
public String getBase() { | |
return base; | |
} | |
public Map<String, Double> getRates() { | |
return rates; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.exercise; | |
import com.fasterxml.jackson.databind.ObjectMapper; | |
import org.apache.logging.log4j.util.BiConsumer; | |
import java.io.BufferedReader; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.InputStreamReader; | |
import java.net.URL; | |
import java.util.HashMap; | |
import java.util.LinkedList; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Optional; | |
import java.util.stream.Stream; | |
import static java.lang.String.format; | |
import static java.util.stream.Collectors.toList; | |
import static java.util.stream.Collectors.toMap; | |
public class SumCalculator { | |
private static final String RATES_URL_TEMPLATE = "https://api.exchangeratesapi.io/latest?base=%s&symbols=%s&access_key=d6943c813d6c3a3d1ca6793e8232a096"; | |
private final ObjectMapper mapper = new ObjectMapper(); | |
public List<Double> sums(InputStream transactions) throws IOException { | |
if(!transactions.markSupported()) { | |
throw new IllegalArgumentException("Given input stream does not support mark"); | |
} | |
transactions.mark(Integer.MAX_VALUE); | |
Map<String, List<String>> currencies = retrieveAllUsedCurrencies(transactions); | |
transactions.reset(); | |
Map<String, Map<String, Double>> rates = getRates(currencies).stream().collect(toMap(ExchangeRate::getBase, ExchangeRate::getRates)); | |
return calculate(transactions, rates); | |
} | |
private List<Double> calculate(InputStream transactions, Map<String, Map<String, Double>> rates) throws IOException { | |
List<Double> results = new LinkedList<>(); | |
return readTransactions(transactions, results, (results1, transaction) -> { | |
String base = transaction.getBase(); | |
String target = transaction.getTarget(); | |
int sum = transaction.getSum(); | |
double rate = Optional.ofNullable(rates.get(base)).map(baseCurrency -> baseCurrency.get(target)).orElse(Double.NaN); | |
double result = sum * rate; | |
results.add(result); | |
}); | |
} | |
private Map<String, List<String>> retrieveAllUsedCurrencies(InputStream transactions) throws IOException { | |
Map<String, List<String>> currencies = new HashMap<>(); | |
return readTransactions(transactions, currencies, (currencies1, transaction) -> { | |
String base = transaction.getBase(); | |
String target = transaction.getTarget(); | |
currencies1.merge(base, List.of(target), (existingTargets, newTargets) -> Stream.concat(existingTargets.stream(), newTargets.stream()).collect(toList())); | |
}); | |
} | |
private <T> T readTransactions(InputStream transactions, T result, BiConsumer<T, Transaction> adder) throws IOException { | |
BufferedReader reader = new BufferedReader(new InputStreamReader(transactions)); | |
boolean header = true; | |
for (String line = reader.readLine(); line != null; line = reader.readLine()) { | |
if (header) { | |
header = false; | |
continue; | |
} | |
Transaction transaction = parseLineToTransaction(line); | |
adder.accept(result, transaction); | |
} | |
return result; | |
} | |
private List<ExchangeRate> getRates(Map<String, List<String>> currencyNames) throws IOException { | |
List<ExchangeRate> exchangeRates = new LinkedList<>(); | |
for (Map.Entry<String, List<String>> entry : currencyNames.entrySet()) { | |
String base = entry.getKey(); | |
String targets = String.join(",", entry.getValue()); | |
String url = format(RATES_URL_TEMPLATE, base, targets); | |
ExchangeRate exchangeRate = mapper.readValue(new URL(url), ExchangeRate.class); | |
if (exchangeRate.getBase() != null) { | |
exchangeRates.add(exchangeRate); | |
} | |
} | |
return exchangeRates; | |
} | |
private Transaction parseLineToTransaction(String line) { | |
String[] parts = line.split(","); | |
return new Transaction(parts[0], Integer.parseInt(parts[1]), parts[2]); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.exercise; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.util.List; | |
public class SumCalculatorCli { | |
public static void main(String[] args) throws IOException { | |
try (InputStream in = SumCalculatorCli.class.getResourceAsStream("/transactions.csv")) { | |
List<Double> results = new SumCalculator().sums(in); | |
System.out.println(results); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.exercise; | |
public class Transaction { | |
private final String base; | |
private final int sum; | |
private final String target; | |
public Transaction(String base, int sum, String target) { | |
this.base = base; | |
this.sum = sum; | |
this.target = target; | |
} | |
public String getBase() { | |
return base; | |
} | |
public int getSum() { | |
return sum; | |
} | |
public String getTarget() { | |
return target; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Base | Sum | Target | |
---|---|---|---|
USD | 100 | ILS | |
USD | 150 | GBP | |
ILS | 100 | USD | |
EUR | 300 | ETH | |
CAD | 50 | KRW |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment