0

I need to iterate over HashMap of HashMap using lambda expressions and filter out few undesired entries. I tried a few approaches but it doesn't seem to work. Below is the structure of the map.

Map<String, Map<Date, String>> sensor_tags = new HashMap<String, Map<Date,String>>();

From this map, I need to remove the entries where sensor data is older than certain date (date is key of inner map). Below is the sample data for the map sensor_tags-

String tagName = "zoneSensor";
Map<Date, String> values= new HashMap<Date, String>();

// (1st entry for tag 1) --> date is day before yesterday
Calendar datekey1 = Calendar.getInstance();
datekey1.set(2018, 12, 01, 12, 30, 45);
values.put(datekey1.getTime(), "ON");

// (2nd entry for tag 1) --> date is yesterdys date
Calendar datekey = Calendar.getInstance();
datekey.set(2018, 12, 02, 12, 30, 45);
values.put(datekey.getTime(), "OFF");

// (3rd entry for tag 1) --> date is today
Calendar instance = Calendar.getInstance();
instance.set(2018, 12, 03, 12, 30, 45);
values.put(instance.getTime(), "ON");

//(4th entry for tag 1)--> date is today + 10 sec
instance.add(Calendar.MILLISECOND, 10000);
sensor_tags.put(tagName, values);

values.put(instance.getTime(), "ON");
sensor_tags.put(tagName, values);

// 5th entry for tag2
tagName = "zoneSensor1";
values= new HashMap<Date, String>();
values.put(Calendar.getInstance().getTime(), "NORMAL");
sensor_tags.put(tagName, values);

//code in java 7
for (final Entry<String, Map<Date, String>> entry : sensor_tags.entrySet()) {
            final Iterator<Date> iter = entry.getValue().keySet().iterator();
            while (iter.hasNext()) {
                final Date date = iter.next();
                if (date.before(givendate)) {
                    iter.remove();
                }
        }
}

// code tried in java 8 (I am a beginner here :-) )
 sensor_tags
            .entrySet()
            .stream()
            .map(Map.Entry::getValue) // get an inner map
            .filter(value -> ((Date)value).before(datekey.getTime()));

How can I iterate over and filter data for inner maps? Also, once the data is filtered, I need to retain structure of the map as is, hence the output needs to be collected in Map<String, Map<Date, String>> only.

3
  • Are you trying to modify the existing map or produce a filtered copy? Commented Dec 3, 2018 at 23:46
  • Need to modify existing map only Commented Dec 3, 2018 at 23:48
  • Why did givendate mutate to datekey.getTime() upon switching from loop to Stream code? Commented Dec 4, 2018 at 8:10

3 Answers 3

5

If you want to modify the existing map, you don't need streams. Just loop and call removeIf():

sensor_tags.values().forEach(
        m -> m.keySet().removeIf(datekey.getTime()::after));
Sign up to request clarification or add additional context in comments.

Comments

2

I have made these two methods for you. The first creates a new hashmap and the second changes your Hashmap on the fly with the desired filtering. I didn't check for corner cases but they seem to work. Hope this helps:

Keeps your hashmap

private Map<String, Map<Date, String>> getFilteredResultsKeeping(Map<String, Map<Date, String>> sensor_tags,
        Date givendate) {
    return sensor_tags.entrySet().stream()
            .map(sensorValue -> new SimpleEntry<>(sensorValue.getKey(),
                    sensorValue.getValue().entrySet().stream()
                            .filter(sensor -> !sensor.getKey().before(givendate))
                            .map(sensor -> sensor.getKey())
                            .collect(toMap(date -> date, date -> sensorValue.getValue().get(date)))))
            .collect((toMap(SimpleEntry::getKey, SimpleEntry::getValue)));
}

Changes your hashmap

private Map<String, Map<Date, String>> getFilteredResultsRemoving(Map<String, Map<Date, String>> sensor_tags,
        Date givendate) {
    return sensor_tags.entrySet().stream()
            .peek(sensorValue -> {
                List<Date> invalidDates = sensorValue.getValue().entrySet().stream()
                        .map(sensor -> sensor.getKey())
                        .filter(date -> date.before(givendate)).collect(Collectors.toList());
                invalidDates.forEach(date -> sensorValue.getValue().remove(date));
            })
            .collect((toMap(Entry::getKey, Entry::getValue)));
}

Comments

2

if you don't want to modify the existing map then there's no need for a stream, you could simplify your imperative approach to:

for (final Map<Date, String> v : sensor_tags.values()) {
      v.keySet().removeIf(d -> d.before(datekey.getTime()));
}

if you don't want to modify the existing map then you can do:

Map<String, Map<Date, String>> result = sensor_tags.entrySet()
     .stream()
     .collect(toMap(
         Map.Entry::getKey, 
         e -> e.getValue().entrySet().stream()
                                     .filter(a -> !a.getKey().before(datekey.getTime()))
                                     .collect(toMap(Map.Entry::getKey, 
                                                    Map.Entry::getValue))));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.