How to convert result of getMembers(Predicate) to array or another iterable?

Consider the following code, as the then part of a MainUI rule, written in ES5 JavaScript:

var alerts = itemRegistry.getItem("Alerts").getMembers(function (item) {
  return item.getType() == "Number" && item.getState() > 0;
});

if (alerts.length > 0) {
  // There are some alerts
  for (var i in alerts) {
    // This block is never reached, I don't understand why.
    var alert = alerts[i];
  }
}

I just can’t seem to get the result of getMembers(Predicate) to iterate.

Alternatively, is there a way to convert the return value of getMembers() (the version with no predicate) to a JavaScript array?

As Item is a Java object, I’m pretty sure the list returned by item.getMembers() is going to be a java object too, and not amenable to javascript actions directly.
As you can iterate the Group as it stands, what do you want another copy for? What are you trying to do, count non-zero member states?

And no way to convert it to one, correct?

As you can iterate the Group as it stands

Actually I can’t (see the embedded comment in my example), hence the question.

I can iterate the result of .getMembers() alright, but not .getMembers(Predicate). :thinking:

Sort of thing you’reasking for?

Not really. That is how I’ve done it, but not how I wanted to do it.

Namely:

var alerts = Array();
itemRegistry.getItem("Alerts").getMembers().stream().forEach(function (item) {
  if (item.getType() == "Number" && item.getState() > 0) {
    alerts.push(item);
  }  
});

// Now I have an iterable (a JavaScript Array) that I can use to do whatever. 👍

But what I wanted to do:

var alerts = itemRegistry.getItem("Alerts").getMembers(function (item) {
  return item.getType() == "Number" && item.getState() > 0;
});

// alerts is some Java object non-iterable in JavaScript. 👎

Both getMembers() and getMembers(Predicate) return a java Set<Item>, so should work the same. Are you sure the Set that gets returned isn’t empty?

Yup. Both its textual representation in the logger and its .length property show that it isn’t empty. Note that the example code in my original post tests for emptiness.

Anyway, I guess that the getMembers(Predicate) overload is not that commonly used so possibly a bug in the JavaScript bindings?

A java Set doesn’t have a length property, you should use the size() method to get the number of elements. Not sure if the JS engine converts it or add that somehow, are you sure it works properly?

Edit:
Have you tried iterating using forEach instead of for (var i in alerts)? That way of iterating might not be supported, and in that case it probably doesn’t work with getMembers() either (in your example you used forEach for that iteration)

If you do not apply the Predicate and just call getMembers() can you iterate over that?

What do you get when you log out alerts.class.toString()? That will tell you what sort of Java Object is being returned. If you can iterate with getMembers but not with getMembers(Predicate) compare the types of the two classes; are they different?

Some looking around shows you should be using for each(var i in alerts). Java 8 Nashorn Tutorial - winterbe

In both cases they return a Set<Item> so there should be no difference in how the two work inside your rule. If neither work with the for loop then the problem is with that, not the two call to getMembers(Predicate).

Possibly. ScriptUtils has a convert method that can be used to convert ArrayLists to JavaScript Arrays (lots of examples on the web) but I’ve not found an example for doing so with a Set. But I imagine it would work similarly. However I suspect you’d end up with a JavaScript Set instead of an array so you’ll want to extract the values to get to the array.

You could always pull out the values of the set as a Java Array by calling .toArray() and then convert the Java Array to a JavaScript array.

1 Like