Deleting specific instances of a flex repeater - keeping "for" loop in sync with instances

I'm using a button script to use system.perspective.sendMessage() to iterate through a flex repeater component and delete specific instances contained therein. Each instance has a property called "Factory", which can be north, east, or west. For example, the below code is roughly what I have written -- I would like this to delete any instance of the "west" factory from the flex repeater:

selectedFactories = ["north","east"]
for j in range(len(self.props.instances)):
		factory = self.props.instances[j].Factory'
		if factory not in selectedFactories:
			del self.props.instances[j]

But I can't get the indexing to work correctly in the "for" loop. I need the index of the loop to be synchronized with the instance number, but these fall out of sync as soon as any of the instances are deleted. If my first instance is no longer instance[0] but instead instance[12], this will be treated as instance[0] in the "for" loop. Hopefully I am making sense... is there a simple workaround? A saw another post on here that uses something like this within the "for" loop instead:

self.props.instances[{0}].format(j)

...but this seems to break my "del" command at the end, as the script thinks I'm trying to delete a function. I feel like there has to be a weird syntax thing I can use here that will fix all my problems. Any thoughts?

In most languages, like Java, you'd use an iterator. Iterators in java let you remove the current element from the collection without losing your place.

In python, the simplest solution is to iterate backwards, so the shortened list hasn't lost the indices you haven't reached yet.

If the list isn't too large, you could simply reconstruct the list with list comprehension, using its if clause to omit the ones you would have deleted.

selectedFactories = ["north","east"]
prunedInstances = [inst for inst in self.props.instances if inst.Factory in selectedFactories]
4 Likes

That's way more elegant, I like it! It is working great now. I have run into another issue (I'll make another thread if I can't figure this out) where every time this script runs, the first thing I want it to do is refresh the flex repeater's instances binding such that the full list comes through before I run through and prune it, like so:

self.refreshBinding("props.instances")

...but the refresh always happens after the rest of the script is completed, so when the script runs, the instances go away like they should, but then they all come back again in a flash due to the refresh delay. :thinking: Ideally the refresh would happen, the script would wait for this to occur successfully, and then move on with the rest of the code.

Thanks again Phil!! The final code is:

selectedFactories = ["north","east"]
prunedInstances = [inst for inst in self.props.instances if inst.Factory in selectedFactories]
self.props.instances = prunedInstances

Wishful thinking. Most bindings, and particularly bindings in Perspective, are fundamentally asynchronous. Using .refreshBinding() is a request, not an instantaneous mandate.

Consider moving the original binding to a custom property, and running this script when that changes. Writing to a property from multiple directions (binding + script) is a recipe for race conditions. (With the explicit exception of bidirectional bindings to user entry components.)