3

I am trying to locate highest and lowest rate within my API monthly rate call, the object BPI has String values and I am a bit confused how to actually count date as for example a key and rate as a value so the iteration can be done.

I would like to use a Map, but not sure how to proceed correctly:

a

Tim Prozorov
  • 69
  • 10
  • Please share your code, what have you tried ? – azro Sep 18 '19 at 20:29
  • What are you looking for? `Map`? Parse the string to `LocalDate`: `System.out.println(LocalDate.parse(datestring, DateTimeFormatter.ofPattern("yyyy-MM-dd", Locale.ENGLISH)));` and parsing `Double` is your home work ;) – Aniket Sahrawat Sep 18 '19 at 20:36
  • @AniketSahrawat thanks man so I can actually split String and work with one half of the string to parse into a date and one part of the string to double right – Tim Prozorov Sep 18 '19 at 21:26

2 Answers2

2

Looks like you have a JSON document as shown below:

{
  "bpi": {
    "2019-08-18": 9304.6179,
    "2019-08-19": 9860.2835,
    "2019-08-20": 9710.7164,
    "2019-08-21": 9138.8615,
    "2019-08-22": 9126.6978
  }
}

As you seem to be using Java, you could use Jackson to parse the JSON document to an instance of a class such as:

@Data
public class ApiResponse {
    private Map<LocalDate, BigDecimal> bpi;
}

Where the bpi JSON property will be mapped to a Map<LocalDate, BigDecimal>.

For parsing a JSON, you could have:

String json = ...;

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());

ApiResponse apiResponse = mapper.readValue(json, ApiResponse.class);;

Then, to find the highest and the lowest values, you can use:

BigDecimal max = Collections.max(apiResponse.getBpi().values());
BigDecimal min = Collections.min(apiResponse.getBpi().values());

In case you need the full map entry, use:

Entry<LocalDate, BigDecimal> max = 
        Collections.max(apiResponse.getBpi().entrySet(), Entry.comparingByValue());

Entry<LocalDate, BigDecimal> min = 
        Collections.min(apiResponse.getBpi().entrySet(), Entry.comparingByValue());
cassiomolin
  • 124,154
  • 35
  • 280
  • 359
  • Hye buddy thank you so much for your answer => Cannot deserialize Map key of type `java.time.LocalDate` from String "bpi": Failed to deserialize java.time.LocalDate: (java.time.format.DateTimeParseException) Text 'bpi' could not be parsed at index 0 – Tim Prozorov Sep 19 '19 at 11:44
  • @TimProzorov I've just updated my answer. Have a look :) – cassiomolin Sep 19 '19 at 11:45
  • it actually worked with Map bpi , so what is the benefit of using LocalDate and BigDecimal if max and min got returned even by parsing to String,String objects (Sorry for my English) – Tim Prozorov Sep 19 '19 at 15:54
  • @TimProzorov You surely could use string only, depending on your needs (I don't know your requirements though). But, semantically, you seem to have values that are dates are decimal numbers, so that's why I suggested `Map` instead of `Map`. Feel free to pick the data type that suits you best :) – cassiomolin Sep 19 '19 at 15:59
0

One of possible solutions could be taking advantage of java.util.regex. Following code will parse your json into 2 groups: the date string and the value string. Then I just put those into Map <String, String> (you can convert String's to whatever type you need):

Map <String, Double> bpiMap = new HashMap();
Pattern.compile("(?m)^\\s*(\\d{4}-\\d{2}-\\d{2})\\s*:\\s*([\\d\\.]+).*");
Matcher matcher = pattern.matcher(json);
while (matcher.find()) {
  bpiMap.put(matcher.group(1), Double.valueOf(matcher.group(2)));
}

Here is the regex decoding for you:
(?m) - multi-line
^ - beginning of the line
(\\d{4}-\\d{2}-\\d{2}) - the first group for the date
\\s - whitespaces
([\\d\\.]+) - the second group for the value

Highest and lowest rates:

Entry<String, Double> hi = Collections.max(bpiMap.entrySet(), Entry.comparingByValue());

Entry<String, Double> lo = Collections.min(bpiMap.entrySet(), Entry.comparingByValue());

Of course this is not the only solution (e.g. parsing in Jackson objects could also be an option), but it appears to me simpler and doesn't depend on third party libraries (like com.fasterxml.jackson).

Note: this solution works pretty well for the json structure as simple as you have described and may not be as beneficial if the structure is more complex. In that case other options like jackson (well described in @cassiomolin answer) may be considered.

Victor Kim
  • 1,647
  • 2
  • 16
  • 33
  • Hey man thanks for sharing and showing some care here, actually the idea is to parse the rate to double so I can the create a stream function that locates the min and max rate value within that period – Tim Prozorov Sep 19 '19 at 10:12
  • Ok, I updated my answer: added how you could extract highest and lowest rates in Double. I still think it may be a better solution than the Jakson objects as it doesn't depend on FasterXML Jackson library (which you have to include in your project otherwise) and instead uses standard java.util.regex – Victor Kim Sep 19 '19 at 23:56
  • Regex surely is [not the right tool for parsing XML](https://stackoverflow.com/a/1732454/1426227) and I also have the feeling that regex is not the right tool for parsing JSON either. I would simply stick to a JSON parser such as Jackson. – cassiomolin Sep 20 '19 at 08:24
  • 1
    I wouldn't go with regex for a complex json structure either, but for as simple structure as described in the question it may serve the purpose quite well without involving third parties. It's an optional solution, but it actually works for this particular problem. You described the Jackson one I described regex - It's better to make considerations among multiple choices. – Victor Kim Sep 20 '19 at 10:04