Skip to content

Instantly share code, notes, and snippets.

@alexradzin
Created June 9, 2022 16:16

Revisions

  1. alexradzin created this gist Jun 9, 2022.
    27 changes: 27 additions & 0 deletions ExchangeRate.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,27 @@
    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;
    }
    }
    10 changes: 10 additions & 0 deletions README.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,10 @@
    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)
    98 changes: 98 additions & 0 deletions SumCalculator.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,98 @@
    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]);
    }
    }
    14 changes: 14 additions & 0 deletions SumCalculatorCli.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,14 @@
    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);
    }
    }
    }
    25 changes: 25 additions & 0 deletions Transaction.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,25 @@
    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;
    }
    }
    6 changes: 6 additions & 0 deletions transactions.csv
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,6 @@
    Base,Sum,Target
    USD,100,ILS
    USD,150,GBP
    ILS,100,USD
    EUR,300,ETH
    CAD,50,KRW