Forum Academy Marketplace Showcase Pricing Features

List Shifter: Reverse, Rotate, Swap and ITERATE (Loop) Over Bubble Lists | Now at v1.4: Adds Numeric Option, GET INDEX Action

Update note! (@boston85719) : Recently published version 1.9.23, which adds a couple of new Range-related functions to the PROCESS List action. These are very similar to the already-existing “Range x.containsPoint(y)” operator. The new ones are:

  • Range x.containsRange(y): returns true when a range (a numeric range or date range) in Operand X contains the range specified in Operand Y.

  • Range x.overlapsRange(y): returns true wen a range ( a numeric range or date range) in Operand X overlaps with the range specified in Operand Y.

Note that, like most other PROCESS List operators, these functions can operate on scalar values (i.e., a single range) or on an array (list) of ranges, allowing you to iterate over a list in a single step, with a couple of caveats:

  1. The list of ranges should be in Operand X. The value you are checking against must be a single range (specified as Operand Y). For example:


    :point_up: here, a list of ranges (stored in LS Type List Constant) is checked against the single date range stored in LS Type Constant.
    The value of Result Step 1 will then be an array of booleans, 1 for each date range in Operand X that is true if the date range contains Operand Y’s range, like [true, false, false, true] . Pro tip: You could then do a step like RS1 Array x.includes(y) true to return true if any of the values is true.

  2. If you specify an array of ranges for Operand Y, this won’t throw an error, but only the first value in the list will be used.

  3. These functions expect a Bubble style range. But note that, in JavaScript-land, a Bubble range is just a two-element array of numbers or dates. (Like: [ [1, 2], [3, 4], [1, 5]] ) If you pass these operators something that’s not a range, this may not necessarily throw an error, however, they will convert the passed value to something the function can deal with. Specifically, if the values you pass are not two-element arrays that look like ranges, it will:
    a. if it’s a one element array, it will construct a range using just that value. E.g., pass it [1] and it’ll interpret that as the range [1, 1].
    b. if it’s a more-than-two element array, it will construct a range using the first value in the array and the last value in the array (note: NOT sorted, but literally the first and last values in the array). E.g., passed [1, 2, 5, 4, 3] as an operand, it will interpret that as the range [1, 3].

A common application of these functions:

Of course, what you’d typically want to do with these functions is to compare two lists of ranges – for example, two lists of date ranges. One list might be a list of date ranges that represents Timeslots today. And the other might be a list of date ranges that represents My Booked Appointments.

And, then you might want to know: Which of the Timeslots overlaps with My Booked Appointments, so that we might show those Timeslots as unavailable.

To do, this you’d put one of the lists into a List Shifter as that List Shifter’s Original List (personally, I’d put Timeslots in there). And then you’d check for collisions using PROCESS List, stepping through the individual ranges in the List Shifter and checking if they have any conflicts with any of the ranges in My Booked Appointments, using Range x.overlapsRange(y).

If the array returned by Range x.overlapsRange(y) contains any true values, that individual Tiimeslot is not available.

We could capture the list of unavailable timeslots by pushing the to List Shifter’s Processed List.

The PROCESS List action would look like this:

Step 1: Check if any of the Booked Appointments overlaps with the current List Item:


:point_up: we would import the list of “My Booked Appointments” ranges via the Anything List Constant field, way down the interface in PROCESS List. Since the data type of our List Shifter’s Processed List (set in the main interface) would be set to “date range”, we could alternatively use the field “LS Type List Constant”. In either case, you’d just build your expression - probably a Search, but it could be a List held in some other List Shifter or Custom State or whatnot - in either of those fields and select it as Operand X.)

Step 2: Check if RS1 has any true values in it:

Step 3: If RS2 resolves to true, the current List Item is unavailable and we could push it to List Shifter’s Processed List:

When PROCESS List completes, List Shifter’s Processed List will contain a list of the unavailable Timeslots.

Of course, you could instead build the list of available timeslots simply by inverting the Push/Sum/Set Only If criterion, like so:

(:point_up:The ! means “not” - this step says, take use the current List Item as the result of Step 3 and push it to the Processed List only if RS2 is false – that is, if it has no overlaps.)

Additionally, this update includes a bit more error checking in the datewise sort of the SORT action (which may allow you to sort by a list of dates that contains some empty/null dates, but I’ve not actually tested that in real life, but this was suggested by another user).

Hi there,

I’ve run into a problem with List Shifter that looks like it might be a bug. I have a LS in a reusable element and a workflow to iterate over a list that can be triggered from outside the reusable element. My goal is that I put a bunch of these on a page so that a user can make several selections of things and then press a button and all these selections get processed via a List Shifter iteration. But when I put several of these in the page and then trigger all of the iterations from the page, the iterations seem to start interfering with each other and several don’t complete.

I created a simple implementation on this editor


Here you can input a bunch of texts in each of the input boxes and they will collect in a list outputted under “Input List”. Each of the reusable elements has a workflow that just iterates over this list and collects the elements in another list under “Output list”. When you click “run iterations” it triggers all the iterations. If you populate more than one of the input elements with about three or more elements, when you click the “run iterations” button you’ll see that some of the elements won’t make it to the output list.

Any ideas on what could be happening here? Thanks a lot!

@keith

Was there some update from Bubble that caused a problem with List Shifter Iterate Function?

I had set things up yesterday, tested and re-tested as I normally do to ensure everything was working before moving along, and everything was.

Specifically, I’m creating a list of time slots

Everything worked, I had a list of dates that displayed as times lots from 12AM to 11:45PM in 15 minute increments.

Now, I am only getting 30 minute increments

I noticed this issue when I noticed an issue with another set up using the iterate function to create a list of days of the week

Yesterday everything was working fine, I had a list of 7 dates from March 1 to March 7…then today it started to show only 6 dates from March 1 to March 6…trying to figure things out I added some pauses and noticed that when running sometimes multiple days got skipped, sometimes March 6 was skipped and showed March 7…just a lot of strange behavior.

My only assumption since everything was working perfectly yesterday (actually just a couple of hours ago) but is now not, is that Bubble did some update that is causing an issue. I tried to restore my application to a previous time to check if I had unwittingly caused an issue, but that didn’t solve the problem.

Any idea what might have sparked this issue? Just seems like items are getting ‘skipped’ in the iterate workflows I set up.

BTW I didn’t change any of these workflows between when everything was working and things got funky.

How would I know?

Just wondering if you maybe had experienced the same thing.

I did some tests and what it seems is there is an issue when I have multiple reusable elements that all have list shifter elements performing some functions.

I created videos to demonstrate the issue.

I’ve tried to set ID elements onto each list shifter to try and mitigate but that has not resolved the issue.

Is there a way to have multiple reusable elements with list shifters on each play nice together that I am missing?

Each video is a few minutes long, but the final 30 seconds is relevant to demonstrate the issues.

This video shows the very first reusable element placed onto a page creating a list of dates successfully with no issues. All 7 days of the week are displayed.

The video shows a single element work to create a list of time slots while on the page and the time slots are working correctly, however the days of the week no longer shows all 7 days and some days get skipped.

This next video shows how more than one element creating time slots on the same page cause an issue in all elements

Hi @keith

I am just getting into these new functions and they are awesome additions.

I’ve been able to utilize it in a workflow action to compare all values in a list of ranges to the same list ensuring there are no overlaps in the single list of ranges. Really excited to get that functionality.

1 Like

I just read your post…seems like a similar issue I am experiencing with multiple List Shifters across multiple reusable elements all placed onto the same page.

I don’t know what your use case is, but sometimes when I’ve experienced an issue, adding a pause helps avoid it.

I actually wrote out a long post about an issue, and after writing decided to check the length of a pause which I set to 10ms originally, then changing it to 100ms the issue has gone away.

Thanks! I tried the pause, but it proved too unreliable a method. In the end my workaround was to include a workflow in the reusable element to set a custom state when the iteration completed, and then on the main page trigger each reusable element iteration only when the once the previous one had ended in separate events. It’s messy, but pretty robust as far as my testing went.

1 Like

Hi @keith

I am experiencing an issue with ‘clearing’ the custom list of a list shifter

I set things up to publish a custom list and left the value empty as per the documentation signaling to set an empty list

I keep getting an error message in the debugger and the custom list value does not clear.

I’ve tried this setting the ‘Publish Custom List?’ value to yes and no, but both ways produce the same error message in the debugger

As a workaround I created a custom state on my list shifter element that is a date range list and by default it is empty and this is working to clear the list.

Seems like there is something causing the empty custom value list to throw an error

Ah, yeah, that would be a bug. Will fix in a future update.

@keith I’m having an issue when using the SORT function on a paginated list. It only sorts the items that are on the current page rather than the whole list. Is there something I can do to change this?

GREAT plugin btw

That’s by design, pretty much. But the solution is to add another list shifter. Put your source list in List Shifter 1. Use the SORT action on that one. Then take the Shifted List output of List Shifter 1 and use it as the source for List Shifter 2 (which you put in Pagination mode).

Whenever you do the SORT action on List Shifter 1, your paginated list from List Shifter 2 will automatically update.

Thanks, got it working

1 Like

Hi there everyone - @keith - this plugin looks awesome. I’m just trying to figure out if this can help with something I’m trying to achieve.

If anyone has any input, I’d greatly appreciate it!

I have a database of companies, each with their specialities in a list field like so:
image

I’d like to make a ranked list of frequently occurring specialities and their counts.
So the action would be to take all selected companies, look at each ‘SpecialitiesArray’ to get all unique specialities, then count. I’ll most likely be using this data in a RG.

  • Live Events: 10
  • Marketing: 9
    etc.

I had a search, but no luck - thanks in advance for any help!

All the best,
Rob

I’m sure there is a way that the list shifter plugin could help with this, but you might also be able to do this with the dynamic expression operator :group by

Not sure how that might work in practice since the Specialities are not their own data type and instead a field of text on the company data type, but you might originally set the repeating group data type to text. Then in the datasource do search for companies’ speicalitiesArray (this may or may not generate a repeating group list of text values equal to the full list of all companies specialtiesarray)

If it does, the next thing would be to click on the datasource dynamic expression again then select ‘more’ to find the :group by operator…again not sure how this would work in practice though as this is a list of text field.

Worth playing around with it to see if it can do what you want.

I’d also recommend watching the videos keith posted describing how the plugin works…while watching the videos it gave me ideas of how to use the plugin for my own use cases.

Hello,

Is it possible to add an item to the first position of a list and save the positions? I can’t use Reverse because I can drag & drop on my repeating group.

I’ve watched the videos but I can’t find the solution.

Thanks

Thanks @boston85719 - that’s really helpful. I have a list of uniques in the way you mentioned.

The only thing is, you can’t group by those. So at the moment each cell of the RG is looking up the occurrences of the word in the list of companies’ specialities fields and counting.

The one thing I’ve not cracked is ordering a group by the calculated field… will do some looking now.

image

See that SORT action that List Shifter has?

Hey @killianAB. Yeah, you can do this with List Shifter. As you’ve noticed, when you modify a Bubble list with “:plus item”, you’re appending the new value at the end of the Bubble list.

If you would instead like to take some list and prepend a value to the beginning of that list, you can do this in List Shifter in many different ways, but probably the best way is using the PROCESS List action. The PROCESS List action and the Processed List output are for situations like this where you desire to construct some entirely new list in a specific way.

The most straightforward way to do this in List Shifter is to use the Array x.concat(y) operator in PROCESS List. This takes two arrays and joins them together with the items in Array X, followed by the items in Array Y. (It’s literally just performing the JavaScript .concat() operation.)

As an example, if I have a list like [1, 2] and another list like [3, 4, 5] I can combine them into a new array [1, 2, 3, 4, 5] with this operator and then publish that new list to List Shifter’s Processed List output.

We can use this feature to take our single item (by first turning it into a list of just that item) and concatenating it with some existing list. Of course, this is simplest if we use a List Shifter’s Processed List output as the place we construct our list all the time. (Which may be what you want to do.) But we can also just take that list and use it somewhere else (like feed it into the List Shifter you’re using to do SWAP items or whatever).

Have a look at this simple example in run mode first:

When you click “Add Item” on any product in the left repeating group, it gets concatenated with the existing list and appears at the top of the list (which I’ve displayed both as a text list of product names and also in a Repeating Group).

The setup for this is pretty simple. If you look at that project in edit mode you’ll see on the “Add Item” button, there’s a workflow with a PROCESS List action set up as follows:

That’s pretty much it, just one step, and instead of iterating over some list, we just “Do Once”.

As you can see our operator in step 1 is Array x.concat(y). So this takes the value in “LS Type List Constant” (which we will set up to be the single item we want to add, converted to a list) and then concatenates the contents of the Processed List on to that.

Then, we need to take this result (which is a new list) and push it to one of List Shifter’s outputs. In this case, the natural thing to do is make the new value of our Processed List this value. We do that by setting “Set Proc List to RS1” to yes as shown here:

A couple more things to note. We supply the item we want to add to the list via the “LS Type List Constant” input. In case this name doesn’t mean anything to you, it means, “a list of List Shifter’s (Process Output) Type”. I’ve set my List Shifter element’s Process Output type to be “Product” (you have to set the Process Output type to use the PROCESS List action).

Scroll WAAAAYYY down the Process List dialog to see the input for the LS Type List Constant field. Here, we set this value to be the Current Cell’s Product (but this could be any product) and then I turn that single value into a list with just that one item using the :converted to list operator:

The other thing I’ve done is set the “Don’t Clear Processed List” option to yes, so that it’s preserved. (By default, List Shifter will clear the Processed List before executing, but we don’t want that in this case, obviously.)

Hi Keith, is the backend part of the plugin ready?