October 29, 2019

Dynamo: String Concatenation 2

I had some time and tested some scenarios using the "successful" nodes for concatenating a list of strings shown in this previous post and noticed that the concatenation will still work if the lists do not all have the same numbers of strings. It still concatenates the first item in each list, and so on; once the shortest list runs out of items, it simply does not contribute anything to subsequent concatenations. The process will fail, if the items in the source lists contain anything other than strings, however. That is not a particular surprise (to me, anyway), but a combination of being able to handle non-text items in the source lists without throwing an error/crashing the graph and wanting to see just what it would take to write a Python Script node that could process a variable number of lists of strings prompted me to try it.

A List Create node is still necessary to make a single list of lists to serve as the input, so that the Python Script node need only have one input, but the List.Transpose and List.Flatten nodes can be eliminated.

The Python code is fairly straightforward.
  • The input list of strings is assigned to lstInput, and lstOutput is set up as an empty list, to receive the list of concatenated strings, which will be the output of the node.
  • A for statement examines the length of each of the sublists in lstInput, to determine the maximum list length (maxLength). This is used to set the number of times the concatenation code needs to be run: once for every item in the longest list.
  • An index counter, iIndex, is initialized to the first index, 0.
  • A while statement is used to process the lists, once for each item on the longest list.
  • On each pass through the while code, a variable to hold the concatenated string from that pass (strConcat) is initialized as an empty string.
  • Then, for each sublist in lstInput, strX is set to the string at the current index being processed. This is done in a try statement, so that the code does not fail when a shorter list has no item at the current index. If a value is returned, an if statement verifies that it is a string. If not, strX is reset to an empty string. If a value is not returned to strX, the except statement sets strX to an empty string.
  • The string value in strX is then added to the end of strConcat, and the for statement is rerun for the next sublist, until all sublists have been processed.
  • With the fully processed concatenated string in hand, an if statement checks the value, to see if it remains an empty string. If not, the string is appended to lstOutput. If it is empty, no action is taken.
  • Finally, the index counter (iIndex) is incremented, and the while statement is run again, provided that iIndex remains less than the length of the longest sublist (maxLength).
  • Once the while statement has been run once for each item in the longest list, the list of concatenated strings in lstOutput is sent to the output of the node.
It turns out that the code required to handle a variable number of lists of strings was not as complicated as I thought before I wrote it. For situations where I am certain that the lists of strings to be concatenated will in fact be lists of strings, I probably would use the out-of-the-box nodes shown in the previous article, as it is easier to read (no "magic" black box of Python code doing who knows what), but in cases where the lists might not all be lists of strings (user inputs?), I might choose to use the Python Script node rather than checking the lists of strings to verify that all items are strings.

October 25, 2019

Dynamo: String Concatenation

This may be basic and blindingly obvious to more experienced Dynamo users, but this is something with which I have struggled for some time, and now that I have finally sorted it out, I wanted to document it here for easy future reference.

The String.Concat node in Dynamo provides an easy way to combine two or more strings into one string.
When each item fed into the stringn inputs is an individual string, all of the strings are combined into one string, and that is the output of the node, as shown in the image above.

I rarely need to combine one set of individual strings, however; I almost always have lists of strings to process. Perhaps there is some deficiency in the way I think, but when I first tried to use the String.Concat node with lists of strings, I was expecting that the the result would be a list of concatenated strings, and that the value at each index in that list would be the concatenation of the strings at that same index in each of the input strings. Unfortunately, that is not what the node does.
As you can see, the result is a list of strings, one for each input, with the strings in each input concatenated together. I was expecting ["abc", "123"], but I got ["a1", "b2", "c3"]. This seems contrary to what it does when single strings are input - at least, it does to me. I found this quite frustrating, and on at least one occasion, wrote a quick Python Script node to do the concatenation the way I wanted it. But that quick Python Script did not allow for varying numbers of input lists of strings (I suppose it could be written to do so, but that would no longer be "quick"). I could not understand why the String.Concat node would not do what I wanted, and could not believe that the way it works is what most users would want, most of the time.

Once I set aside my frustration and started thinking again, it dawned on me that all I needed to do was rearrange my input data to work with the way the String.Concat works, and while that requires a few extra nodes, it is fairly easy to do. (Click on the image below to see it full-size.)
By using the List Create node, I combined my individual lists into one list of lists. The List.Transpose node then transforms that list of lists into another list of lists, in which each sublist has a list of the items from the same index in the original sublists. So each sublist has a list of the strings I want combined into one string, and this can be fed into the String.Concat node to get the desired concatenation. The output of the String.Concat node ends up having the list of concatenated strings nested in a list, so adding a List.Flatten node using the default flatten amount (totally flatten) results in a list of the concatenated strings, which was my desired result.