List Shifter: Reverse, Rotate, Swap and ITERATE (Loop) Over Bubble Lists | Now Part of Floppy

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).