How to sort a list of values and names

Hi folks,

I’m having trouble sorting a list of values and names. The objective is to archive the following:
I get distance values for BLE beacons from 3 different ESP32 via MQTT. For each beacon I want to figure out to which ESP32 it is closest to and get the room name. I’m not very familiar with java so I ‘frankensteined’ a peace of code from this forum yet I can’t figure out how to do the sorting. My idea looks like that:

import java.util.HashMap
import java.util.Collections

rule "Raumanwesenheit Lars"
when
    // Wenn ein Distanzwert sich ändert
    Item SMW_Lars_Kueche_Distance changed or
    Item SMW_Lars_Wohnzimmer_Distance changed or
    Item SMW_Lars_Schlafzimmer_Distance changed
then
    // Neue HashMap anlegen
    val HashMap<DecimalType, String> distancesLars = new HashMap

    // Wenn einer der Distanzwerte nicht NULL ist
    if( (SMW_Lars_Kueche_Distance.state as DecimalType) !== NULL || 
    (SMW_Lars_Wohnzimmer_Distance.state as DecimalType) !== NULL || 
    (SMW_Lars_Schlafzimmer_Distance.state as DecimalType) !== NULL ) {
        // Die Distanzwerte mit dem dazugehörigen Raum in die Liste schreiben
        if( (SMW_Lars_Kueche_Distance.state as DecimalType) !== NULL ) {
            distancesLars.put(SMW_Lars_Kueche_Distance.state as DecimalType, "Kueche")
        }
        if( (SMW_Lars_Wohnzimmer_Distance.state as DecimalType) !== NULL ) {
            distancesLars.put(SMW_Lars_Wohnzimmer_Distance.state as DecimalType, "Wohnzimmer")
        }
        if( (SMW_Lars_Schlafzimmer_Distance.state as DecimalType) !== NULL ) {
            distancesLars.put(SMW_Lars_Schlafzimmer_Distance.state as DecimalType, "Schlafzimmer")
        }
        
        // Here the trouble begins...
        // Distanzwerte sortieren und Raumname mit der kleinsten Distanz auslesen - hier gibts Probleme
        val distancesLarsSorted = Collections.sort(distancesLars)
        val roomName = distancesLars.get(distancesLarsSorted.get(0))

        // Belegten Raum in das Item schreiben
        SMW_Lars_Occupies.postUpdate(roomName)
    }
    // Wenn alle Distanzwerte NULL sind 
    else {
        SMW_Lars_Occupies.postUpdate("Abwesend")
    }
  
end

Unfortunately that doesn’t work. I’m getting the following error:

Type mismatch: cannot convert from HashMap<DecimalType, String> to List<Comparable<?>>(org.eclipse.xtext.xbase.validation.IssueCodes.incompatible_types)

What am I missing here?

Thanks in advance for any clues on getting that work.

Greetings… Lars

I’m pretty sure there is no method .sort on collections. At least, it is not mentioned in Oracle Help Center…
So, I guess you would have to create a function first…

But maybe there is a much more convenient way, by using groups. Just create a group gLars_Distance and put all thos SMW_Lars_xxx_Distance Items in this group (no need for Aggregation nor special Group Item Type), Now, in the rule:

rule "Raumanwesenheit Lars"
when
    Member of gLars_Distance changed                                                                  // if at least one value changed
then
    var String strRoom = ""
    if(gLars_Distance.members.filter[i|i.state instanceof Number].size > 0) {                         // at least one value valid
        val shortest = gLars_Distance.members.filter[i|
                           i.state instanceof Number].sortBy[ state as Number ].head                  // get a list of valid bvalues, sort from status, get the first item
        strRoom = shortest.name.split("_").get(2)                                                     // get the room name
    } else {                                                                                          // if no valid value
        strRoom = "Abwesend"                                                                          // set to absent
    }
    SMW_Lars_Occupies.postUpdate(strRoom)                                                             // write to item
end

EDIT: Changed sortBy[] Parameter

If you must sort a hashmap rather than the Items that you started with, some suggestions here

You cannot sort a Map. But you can take the key set and sort this. Because the keys are numbers, they are comparable and can be sorted.

val sortedKeys = distancesLars.keySet().stream().sorted().collect(Collectors.toList());
val firstKey = sortedKeys.get(0);
val firstItem = distancesLars.get(firstKey);

There is on the Stream API which is what Rules DSL exposes.

Though it’s called sorted.

Collections also has a static sort method. Collections (Java SE 11 & JDK 11 ) which could work.

But the problem is @gooselk, you have a Map, not a List. What does it even mean to sort a Map? Do you sort by the values? By the Keys? Both? The link provided by @rossko57 shows lots of your options as does @franks. I’ll provide one more.

The biggest difference between mine and franks’ solution is I’m pretty sure that Rules DSL does the stream stuff for you. So you could do something like:

val sortedKeys = distanceLars.keySet.sorted

Hey guys,

thanks for the input so far. As I said, I am quite new to all this and have put the code together in a way that made sense for me. Yet I do not seem to have my head wrapped around the concepts of Maps and Lists yet…

@Udo_Hartmann

Your solution looks promising yet I do get an error on this line stating:

Bounds mismatch: The type arguments <Item, State> are not a valid substitute for the bounded type parameters <T, C extends Comparable<? super C>> of the method sortBy(Iterable<T>, Function1<? super T, C>)(org.eclipse.xtext.xbase.validation.IssueCodes.type_bounds_missmatch)

Did I forget to include something?

@franks

I also tried to use your solution but I do get the following error:

The method or field Collectors is undefined(org.eclipse.xtext.diagnostics.Diagnostic.Linking)

Any advice on that?

@rlkoshak
Your solution also gives me an error:

The method or field sorted is undefined for the type Set<DecimalType>(org.eclipse.xtext.diagnostics.Diagnostic.Linking)

I’m sorry guys, but i think I’m missing something very basic here. So please bear with me.

Any further advice would be very much appreciated.

Greets … Lars

Collection How it works
List An ordered collection of Objects.
Set An unordered collection of Objects.
Map A collection of key/value pairs. The key is used as the index to retrieve the corresponding value.

You are using a Map. There’s a key and a value.

You need to import Collectors.

import java.util.stream.Collectors

As I mentioned, a Set is unordered. So it seems it doesn’t make sense to try to sort it. Rules DSL sometimes falls down on doing this sort of type checking so using the stream like @franks demonstrates is probably the best approach.

Hmm… Maybe you’ll have to add spaces:

val shortest = gLars_Distance.members.filter[i|i.state instanceof Number].sortBy[ state ].head 

Sometimes a bit tricky, but should definitely work.

I think state objects are not directly sortable, you have to give a hint
...sortBy[state as DecimalType]...

Yes, right…

As a status is a status…

Changed the code above :slight_smile:

Good Morning,

thank you all for your help. All your tips did the trick :+1: