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

Another lil’ update: List Shifter v 1.9.22 fixes the “equals” operator in PROCESS List to work correctly for Things. (Basically, this is an extension of the work I started with 1.9.20.)

(Previously, if the items in question were Things, the equality operation would incorrectly return “false”. Now, if the two operands are Things and are, in fact, the same Thing [which is to say, their Unique IDs are the same], this operator correctly returns “true”.)

Hi @keith, I have been through the posts and watched a few videos but I couldn’t understand if it is possibile to iterate a list as an action inside another iteration. What I am trying to achieve is to print nested RG using PDF Conjurer by @vini_brito. So far I have been able to loop on the first RG, but then there is a second level inside it that I need to iterate too. Apparently it does not work, but after hours of tests I would like to ask you if it is possible in your opinion to do that. Thank you.

Good day guys!
I wanted to solve my problem in this case:


with great plugin List Shifter
But, i stucked and can’t solve one problem with adding data in the list
I am creating “Checkout” functionality with text message which contains all data for making order
- In this screenshot i am trying to add iterated first item from RG (Why first? Because if i add whole list, list shifter will add it one-by-one (not in 1 list)). I am counting to identify quantity of data fields
Then, i added “Make changes to thing” to add additional iterated things with conditional that new data type is created and i should just add iterated items:

Here, i stucked. Because when it Makes changes, it adds First item in Repeating group instead of second. How can i solve this problem guys? Should i use state or smth?

Hi @keith

I am getting some errors reported while using the list shifter plugin.

I currently have six list shifter elements on the page, and all are set up the same way to use groupings

Screen Shot 2020-11-09 at 11.43.47 PM

From the error messages it seems that only 3 of the 6 are throwing an error.

I am not using them on page load, but the error messages appear on page load.

Any ideas what it might be?

Anybody know why list shifter ‘list’ values do not compute into a list using Javascript?

When I take a list shifter value and place it into a repeating group or into a custom state as a list it works fine and those values are returned as lists and you can see the count of the list items is correct.

For example if I take a list shifter list of dates and put it into a custom state of type date as a list the list will have the appropriate number of items (7 for a week).

However, when I put it into Javascript to run a chart the value returned is a single value with all items separated by a comma. So when trying to draw a chart using the list shifter values I only get one data point instead of seven.

Also what is strange, is if I set the custom state on the page using the list sifter value, in Javascript it does the same thing. However, when I use a repeating group and put that into Javascript I have no issues.

Any ideas of how to ensure while running javascript using the list shifter original list values that it will return a list of single values instead of a single value that shows a list?

Yes. You’re probably attempting to hit List Shifter with an action before it’s initialized. Don’t do that.

Hey Keith,
Thanks! For making this plugin! It’s great so far.
I’m trying to sort a list of Data Type A which based on the results from an API call that returns the ordered list of Data Type A’s uniqueID and trying to . Without knowing exactly how the plugin is running, is there a way for me to sort the Data Type A by the list of uniqueIDs without needing to convert back and forth between objects?

Hey @boston85719: When you’re using ANY Bubble list in Run JavaScript, you’ll have this same issue.

What Run JavaScript does is takes a string (a text in Bubble) and runs the JavaScript function eval() on it.

The eval() function lets us take a string and interpret it and execute it as JavaScript.

Of course, the interface to Run JavaScript is a long text entry box. When you use dynamic values there, what happens is that the Bubble value in question is forced to its string representation. (Just like if you dropped a text element on your page and used the dynamic expression there.)

So, if for example we have an expression that’s a list (let’s say it’s a list numbers 1, 2 and 3), this becomes a string of characters like this;

1, 2, 3

(BTW, a list of texts containing the same characters would become the same.)

Similarly, a list of dates will become a comma-delimited string of text representations of those dates (keep in mind that you can control how those dates are represented using :formatted as).

I can answer more about this, but the above is what you have to consider. Back to our list of numbers example, you could construct something like the following in your script:

var a = [Dynamic_List]

And this would at runtime net you:

var a = [1, 2, 3]

… which would turn your textual list representation back into an array of numbers stored in variable a in your script.

The hassle of dealing with lists in custom scripts is in part why List Shifter has the PROCESS List action and why the PROCESS List action has an eval(x) operator — which is exactly the same as Run JavaScript, but allows you to access List Shifter’s internal list values as names variables in your script.

See the documentation and “pro tip” underneath the “long text constant” field in the PROCESS List action dialog.

Thanks for the thoughtful reply. Always appreciated and helpful.

I had thought I set things up properly. My original method of using the repeating group I had things set up like this:

Screen Shot 2020-11-13 at 1.58.52 PM

which worked fine.

Then I set up a version using list shifter and was getting the issue I described. I had basically copied the javascript and replaced the repeating group reference with list shifter and had the issue.

I went in and created a basic test after reading your reply, again copying the javascript and swapping data holder references and I got this result

The left is repeating group and right is list shifter…No difference

So, I took a closer look at how I set this up.

When I posted originally, this is what I got as a result:

Screen Shot 2020-11-13 at 2.03.21 PM

using this javascript

Screen Shot 2020-11-13 at 2.03.12 PM

After reading your reply I focused my attention more and realized that I was missing quotation marks around my comma in the join with

So using this javascript

Screen Shot 2020-11-13 at 2.04.57 PM

I get this result

Screen Shot 2020-11-13 at 2.05.11 PM

Completely missed the quotation marks previously as the culprit.

I know what my day is going to be spent doing now.

Thanks again for the help.

1 Like

Totaly Awesome! Amazing!
Thanks @keith for this powerful plugin.

Hi @keith

I’ve been attempting to get a functionality for a couple days with no luck. I feel that the list shifter plugin would be able to solve the issue, but unsure of how to utilize it correctly to do so.

I have a list of dates. I want to take that list of dates, and change the time by a specific number of minutes (using a dynamic expression as the number of minutes is variable) so that I can create a list of date ranges.

The date range list would have start times of the original list of dates and and end time of the altered date that is changed by a number of minutes.

I’ve been stuck on how to create a list of date ranges from a list of a single date…everything I try to put together doesn’t give me the ability to do so and I get stuck with red expressions. I’ve looked into using listshifter to iterate over a list or to process a list but really have no idea how to use the newdate(x) operation in the process list.

Any tips on how to create this list of date ranges?

Hey @boston85719, here’s an example:

Runtime: https://list-shifter-demo.bubbleapps.io/version-test/build-date-ranges?debug_mode=true

Editor: https://bubble.io/page?type=page&name=build-date-ranges&id=list-shifter-demo&tab=tabs-1

This page has a Calendar Grid Pro element on it to give us a list of dates:

  • The hot pink List Shifter takes a list of dates from that CG Pro as its Original List.
  • We also configure the List Shifter’s Process Output Type to be date range.

Now List Shifter’s PROCESS List action can be used to operate on the list of dates and construct your desired list of date ranges, which can be pushed to List Shifter’s Processed List output.

I’ve also put an input on the page. This input lets you enter a number of minutes. These minutes will then be (1) added to the original dates and (2) used to construct a date range (starting with the original date and ending on the incremented date).


How you set this up in PROCESS List is a little tricky and I can see why it wouldn’t be obvious.

Examine the Custom workflow “Compute Date Ranges with Processed List”:

This action takes some list (in this case, List Shifter’s Original List - the dates shown in CG Pro) and then performs the processing steps on each element in the list.

What we desire to do for each date is:

  • Take the original date and add the number of minutes to it that we specified in the input
  • Construct a date range from the original date and the modified date
  • Put that new date range into a list we can access

PROCESS List lets us do that. Here’s an explanation of the steps:

Step 1:

Take the current list item (which is a date) and convert it into its time representation in milliseconds (this is that getTime() does – this representation is also called “Unix time”).

This value is now stored as the result of Step 1. Internally in List Shifter, this is called “RS1” or “Result Step 1” and we can select it from the dropdowns in future steps.

Step 2:
Add the number of minutes we desire to that.


So now we take Result Step 1 and add (+ operator) the number of milliseconds.
For the second operand, we’d put “the number of minutes from the input times 60 times 1000” to get the number of milliseconds we need to add.

While we can build that expression in Bubble, we unfortunately cannot build that in the “Step Operand Y” field here. (This is because the type of these input fields is tied to the type set as List Shifter’s Process Output type, which is date range in this case. So you could only build a date range type expression here, but we need a number right now.)

So, rather than put the expression here, we define it further down in the Process List dialog. You’ll see for Step Operand Y I’ve selected a value from the dropdown: Numeric Constant.

If we need differing types of values like this, we define them down at the bottom of the PL interface. Here’s our setting for Numeric Constant (scroll WAAAAYYYY down!):

And there, just as you’d expect, is our math for converting the minutes specified in the input into milliseconds (value * 60 seconds/minute * 1000 milliseconds/second giveus us value in milliseconds, right?).

Anyway, the net result is that we take the original time, add some number of milliseconds to it and that value is now “RS2” or “Result Step 2”.

On to Step 3:


Now we take the result of step 2 (which is currently a number) and turn it back into a date, using the new Date() operator.

Now we have “RS3” or “Result Step 3” which is a date.

Step 4:
Now that we have two dates, we can construct a date range from them. We’ll take the original List Item as our start and RS3 as our end.

PROCESS List has a handy “make date range” operator that can construct the new date range for us, given two dates. The start is Operand X (here defined as the original List Item) and the end is Operand Y (here defined as Result Step 3, the new date we just constructed).

Now we have “RS4” or “Result Step 4”.

Being done with our computation, we now need to do something with RS4. What we want to do is push is onto List Shifter’s Processed List. We do that by setting “Push RS4 to Proc List” to yes.

We are done with our setup on the PROCESS List action! (We didn’t even need to use steps 5 and 6! Woot!)

Now, all we do is trigger that PROCESS List action whenever List Shifter’s Initialized/Updated event triggers OR when the Input is changed to a new value. That’s why I put the PROCESS List action in a Custom Workflow, so I could trigger it on those two different events more easily.

There ya go…

PS: BTW, you might notice that I clicked the “Debug Mode” checkbox in PROCESS List. This causes a bunch of logging to the console so you can see what PROCESS List is doing internally. This is useful when you’re constructing a new recipe in PROCESS List because it’s kinda like building a ship in a bottle, otherwise!

2 Likes

Thank you very much @keith…as always the clear explanation makes it incredible easy to implement, the example in editor is above and beyond. Also, this has really helped me overall to understand how the process list works, plus watching the video again solidified that.

I do have one further question about a possible implementation that I am not sure if it is possible.

I have looked through the operands choices and don’t see anything that really jumps out to me as a function to find a date range that overlaps with the processed list items.

My first thought was to expand what you provided to a 5th step and use the result of step 4 (the list of date ranges) and search my D.B. for bookings whose date range overlaps to output a finalized processed list of date ranges that are still available.

What I did manage to implement was a set up to use the iteration function.

I had to add two other list shifter elements

Screen Shot 2020-11-21 at 1.38.12 AM Screen Shot 2020-11-21 at 1.37.57 AM

Then in my workflows I used the following actions

the custom state z-date-range-list is a list of date ranges

Then my repeating group used to display available times slots is set up like this

This is working and I am getting the correct available time slots no matter what time interval is set for the meetings being selected, however, it is just not as fast and elegant as I suspect it could be by just adding another step in the process list action.

Is there an operator that could filter the results of step 4 by the bookings in my DB?

Does the list shifter work only with “data types” and not “option set”?

I am using it for a list of option set items, and it doesn’t seem to be working. I am unable to think of any other reason. In the debugger the list’s “List to shift” shows right value, but “original list”, “shifted list” show empty.

Hey @boston85719, there’s a range contains point function in process list, but that’s it right now. If you go examine my demo page again, I show how you can use :filtered to iterate over a list of ranges to find ranges that overlap with that single value.

For the case of a list of ranges, you can put those ranges into a repeating group and use a filter expression like this to disable selection of individual ranges (which are then in their own cell).

You can of course do the same thing using List Shifter ITERATE to perform the same computation and assemble a list of dates that do (or do not) intersect in a workflow.

Note that you can use the SET Custom Value/Custom List actions to update the Custom List without publishing it each iteration for maximum speed.

There’s no special API to Option Sets, AFAIK, so if Option Sets can be resolved to regular old Lists then that’ll work. I’ve not played with Option Sets in this context. Can option sets be resolved to lists of values using :formatted as, perhaps?

So, here is the context:
I have implemented a multi select checkbox kind of thing using a repeating group on option set items.

This repeating group has a custom state “selected items”.

I wanted to perform “reverse” on this “selected items” list as I wanted last added on top. For that I created a list shifter object and made its data source as the “selected items” of the repeating group.

But when I access list shifter’s original list in another element, it is coming blank.

Maybe for this simple use-case I’ll do it via some other technique (by using extra variable etc.) instead of using list shifter.

Thanks for the tip on that. I set up a custom list

Which is the same custom list I was setting on the bubble custom state list I created…it is a bit faster.

To make it seem even faster I use a action for when the iteration is complete to show the list of available time slots.

Thank you again for the help on this…has gotten me the desired UX I was seeking.

Cool, glad you got that to work! BTW, I took a peek at the code for List Shifter and it looks like I had intended to add additional range functions to Process List like “Range x contains range y” and “Range x overlaps range y”, but hadn’t finished them yet. Will add those to the next update.

Those would be awesome additions.