Skip to content

Instantly share code, notes, and snippets.

@alexradzin
Created June 9, 2022 16:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexradzin/bfe219b653d1eea2cdd1f848c720059d to your computer and use it in GitHub Desktop.
Save alexradzin/bfe219b653d1eea2cdd1f848c720059d to your computer and use it in GitHub Desktop.
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)
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;
}
}
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]);
}
}
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);
}
}
}
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;
}
}
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