How do you loop through records (things) in a table (data type) in bubble?

I’m finding how to loop through lists, recursion and everything but just looping through things in a data type.

1 Like

This is my basic explanation

There’s some other (probably better) examples
http://forum.bubble.io/search?q=recursive%20workflow

Ha ha, @bryan.holmes, it’s your lucky day because this is a topic that is completely fair to complain about! :tada:

Basically, Bubble has no concept of explicit loops, even on the front end/browser/client-side. This might seem completely bonkers (and, at this point, it is), but there are some explanations for this.

There are no explicit looping constructs in Bubble. No explicit for, for… in, for… of, while, or do… while loop constructs like we have in JavaScript.

Bubble seems to have been conceived as an array-oriented environment (it’s hard to simply call it a “language”, though there are language-like components [the expression builder and workflows to a certain extent] and this is true of all point-and-click development environments).

Which is to say, that it seeks (though fails miserably) to eliminate the need for explicit loops in favor of implicit iteration. (But it does not have the full complement of iterable support that we find in modern JavaScript and this can lead to very frustrating and silly workarounds if one is determined to stick with fully vanilla Bubble to get things done.)

There is, in fact, iteration going on all over the place in Bubble, but again there are no explicit loop constructs anywhere in vanilla Bubble. There are some explicit iterative capabilities, and many implicit ones. The closest thing is on the backend where (as @tylerboodman points out) there are recursive backend workflows. This feature is a fairly recent addition (in Bubble terms).

On the client-side, one can also achieve recursive workflows in vanilla Bubble though these are somewhat fraught with peril (“Jane, how do you stop this crazy thing!?”) and not particularly performant, with respect to their native JavaScript equivalents.

But back to the array-oriented nature of Bubble: In a fully fleshed-out array-oriented environment, we would have no need for looping constructs at all. Anything we needed to do iteratively could be achieved with built-in operators of some sort.

And, to a large extent, Bubble supplies these (but also lets us down in some very fundamental areas). Note that array-oriented languages are weird to the uninitiated. One might ask: “If there’s no for loop, how do I do something (for example) 10 times?”

The array-oriented language answers: “All arrays are implied loops. Because here in our language, our arrays are iterable. Do you have an array of 10 elements? Do something for each element of the array! There’s your for loop!”

And we say, “Oh, OK, I get it. I’ll just make a list of 10 numbers and iterate over that.”

Of course, in vanilla Bubble we do not have an efficient means of making such a list. Womp womp. Plugin time!

But in other ways, Bubble is a nifty array-oriented language and all of these features are implemented in the various operators that are found on lists. And many common operations that we might typically think of as necessitating looping are solved through the various list operators.

As an example, I have two lists. I want to know if they share any elements in common. (Our first thought might be to loop over the two lists, seeing if any two items are equal.) But in Bubble, this is just:

list1 :intersect with list2

The result will be all of the elements shared by the two lists. If the resulting list has no items (that is, if its :count is 0), then the lists share no items in common.

There are zillions of examples like this.

Another reason we might think we need to loop over some object is to get some specific property of it. However, the expression builder syntax for Things has a built-in map operator that’s completely transparent to us. Consider I have a list of Users and now I want the first names of all the users in the list. That’s just:

some_list_of_users's :each item's First Name

Cool! (In JavaScript this would be some_list_of_users.map(user => user.firstName)) Granted, it’s almost the same thing.

Which brings us to some of the ways that Bubble lets us down with respect to being a fully-fledged array-oriented language: Most notably, Bubble is severely deficient in terms of array-oriented math. Consider the following expression:

some_list_of_numbers * 2

This is not a valid Bubble expression, but in an array-oriented language, what else could this expression mean but “take each number in the list and multiply it by 2”? We can’t do this in Bubble.

(In JavaScript, this is just an application of the .map() method: some_list_of_numbers.map(number => number * 2). Bubble does not have a generic map operator like this. Womp womp.)

Similarly, we might have two equally sized lists (let’s say one is a list of prices and the other is a list of quantities, as we might have in a shopping cart type of use case) and want to say:

list_of_prices * list_of_quantities

… to get a “list of subtotals”. But again, this is not a valid Bubble expression. (In an environment that only supports one-dimensional arrays, what could this possibly mean except, “take each item in the first list and multiply it by the corresponding item in the second list”?)

(In JavaScript, this is simply: list_of_prices.map((price, index) => price * list_of_quantities[index]))

The fact that features like this aren’t built out is pretty weird and is a big weakness in Bubble that is filled by various plugins.

On the backend, we have things like do an API (backend) Workflow on a List, which is a natural way for an array-oriented environment to let you iterate over some list, albeit slowly. (The lack of the numeric operations I mention before leads to a great deal of silliness like people sending such operations to a backend workflow when the client-side browser could perform millions of such operations in mere milliseconds. There are non-vanilla-Bubble solutions of course, such as those I talk about here.)

There’s one further complication to all of this in that Bubble’s list operators aren’t really array operations, but they are “set” operations. When you use them, any array with duplicate values in it turns into an array with any duplicates removed (it’s like you turned it into a JavaScript Set, a collection of unique values, which may in fact be exactly what happens under the hood).

This makes sense for Things in the context of the types of web apps that Bubble is designed to facilitate. But it’s crappy for math, right?

There are also some pretty obvious omissions from the available list operators. While features like filtering are pretty robust, there’s no arbitrary .map() or .forEach() type of operator. And common array operations like “indexOf” (tell me the index of some specific item in an array) are strangely absent.

Most of this is would not be difficult at all to build out but just doesn’t get any attention for reasons that don’t make any sense to me (these are not at all difficult or time-consuming to build out). At least we got parentheses in expression recently!

There are cases where a single plugin action (like Process List in List Shifter or the various actions in Floppy) are not sufficient (for example, if as part of our iterative operation we need to do some database lookup that we cannot predict ahead of time) and then features like List Shifter’s “Iterate” actions or Floppy’s “Step Mode” are useful. But there’s no equivalent to these in vanilla Bubble. (Though I advise people to avoid such workflow-looping techniques whenever possible as they are very inefficient.)

But anyway: tl;dr one doesn’t loop in vanilla Bubble.

9 Likes

Thx for knowledge Keith I learned things today

And then gives out without any warning or indication half way through the list with no way to send an email or anything…

1 Like

Ha ha. Yeah, as soon as you start thinking about this stuff it’s easy to list out odd/disappointing Bubble stuff.

But on the plus side another place where the list operators have really improved is in the :format as options. There’s basically no common string (text) transformation that you can’t do now right in the expression builder. So hopefully math operations might similarly improve (though I’ve been not-holding-my-breath on that for a long time). I wrote Process List in the hopes that it would be made obsolete pretty quickly, but here I am releasing new versions of the same idea years later.

2 Likes

That was helpful to understand. Thanks.