Forum Discussion

EnTerr's avatar
EnTerr
Roku Guru
9 years ago

"Join()" counterpart to roString.Split()? [done!]

I found myself wishing today there was the opposite function to ifString.Split() - i.e. if Split() "explodes" string to pieces, there be a function that "implodes" pieces to a string.

A good illustration of the need is this thread viewtopic.php?f=34&t=96379 - say i am writing a url-encode function - can the new string be constructed by appending character after character - sure, but that's a lot of churn. Instead if i can blow the string into array of 1-char strings, replace some of them and join them into one, that would be fancy.

So, a proposal - to define a function or method that can do that. Something with signature like:

  1. ifEnum.joinToStr(sep as String=""), e.g. .joinToStr()

  2. ifStringOps.join(list as ifEnum), e.g. )

  3. ifGlobal.strJoin(sep as String="", list as ifEnum), e.g. )
How would such a mythical creature work? Enumerate the collection, calling toStr() on each element and then join them with separator between (i threw ifEnum and toStr() in in attempt at generality).

Discussion: (3) probably not likely to happen for concerns of polluting the global namespace. (2) is the tack Python takes, it's pure in the sense keeps the method in the String namespace but in my experience confusing to beginners (as me at the time) whose intuition is the reverse method to be invoked on the array, i.e. (1).

Just an idea, it's a "nice to have" feature.

8 Replies

  • Strings are slow, byte arrays are faster.

    Sub Main ()
       s = string(30000, "s"): tm = createObject("roTimeSpan")
       tm.mark(): ss = s.split(""): ? tm.totalMilliSeconds()
       tm.mark(): join1 = joinSlow (ss) : ? tm.totalMilliSeconds()
       tm.mark(): join2 = joinFast (ss) : ? tm.totalMilliSeconds()
    End Sub

    Function joinSlow (strArray As Object, sep = "" As String) As String
       joined = ""
       strArrayLen = strArray.Count ()
       If strArrayLen > 0
           joined = strArray [0]
       End If
       For i = 1 To strArrayLen - 1
           joined = joined + sep + strArray [i]
       End For
       Return joined
    End Function

    Function joinFast (strArray As Object, sep = "" As String) As String
       joined  = CreateObject ("roByteArray")
       sbytes  = CreateObject ("roByteArray")
       sepba   = CreateObject ("roByteArray")
       sepba.FromAsciiString (sep)
       strArrayLen = strArray.Count ()
       If strArrayLen > 0
           joined.FromAsciiString (strArray [0])
       End If
       For i = 1 To strArrayLen - 1
           sbytes.FromAsciiString (strArray [i])
           joined.Append (sepba)
           joined.Append (sbytes)
       End For
       Return joined.ToAsciiString ()
    End Function



    ------ Running dev 'Junk' main ------
    457
    6958
    462
  • Good grief - i had forgotten indexing Roku strings is O(len) and not O(1):
    BrightScript Debugger> s = string(30000, "s"): tm = createObject("roTimeSpan")
    BrightScript Debugger> tm.mark(): for each ch in s.split(""): next: ? tm.totalMilliSeconds()
    97

    BrightScript Debugger> tm.mark(): for i=1 to len(s): ch = mid(s,i,1): next: ? tm.totalMilliSeconds()
    2614

    26x slower? That's +1 argument for using the explode/implode approach!
  • I apologize for being ignorant, but could FormatJson() help in any way?
  • "Komag" wrote:
    I apologize for being ignorant, but could FormatJson() help in any way?

    How so? Let me draft some sample code - say rot47 "cipher" - can you make it work with formatJSON() :
    function rot47(s):
     a = s.split("")
     for i = 0 to a.count()-1:
       j = asc(a[i]): if j > 32 and j < 127 then a[i] = chr(33 + ((j + 14) mod 94))
     next
     return "".join(a) 'make-believe method'
    end function
  • "belltown" wrote:
    Strings are slow, byte arrays are faster.

    Sheesh... you Sir just brought a roByteArray into a String fight!  ๐Ÿ™‚
    Even with byte arrays though, join is ~3x slower than it would be native.

    On a side note, i can 2x speed up your joinSlow() as:
    function joinStrArr(strArray as object, sep = "" as string) as string
       joined = ""
       for each s in strArray:
           joined += sep + s
       next
       return joined.mid(len(sep))
    end function

    PS. bugfix, "return joined" -> "return joined.mid(len(sep))"
  • "EnTerr" wrote:
    On a side note, i can 2x speed up your joinSlow() as:
    function joinStrArr(strArray as object, sep = "" as string) as string
       joined = ""
       for each s in strArray:
           joined += sep + s
       next
       return joined
    end function



    But then you'd end up with a spurious separator at the start of the joined string.

    Both my functions could be further optimized though, e.g. handling the special case separately where the separator is the empty string.
  • "belltown" wrote:
    But then you'd end up with a spurious separator at the start of the joined string.
    Oops. Trivial bug, fixed by "return joined.mid(len(sep))"

    Both my functions could be further optimized though, e.g. handling the special case separately where the separator is the empty string.

    ... or in fancy future, array join function can be added to B/S. Thinking of it, I'd like to propose a generous criterion like
    "If all four of Lua, Python, Ruby, Javascript have a certain utility function, so should BrightScript"
    (of these, Lua is the most spartan - so if even Lua has it... wink-wink, nudge-nudge)
  • i notice that roArray.Join(separtor) has appeared in 7.5, maybe as answer to this question. Funny that someone forgot to mention it in the Release Notes. Was it for fear to set precedent of listening to outside developers, i wonder?

    Brightscript Debugger> ? "*" + ["bing", "bing", "bong"].join(" ") + "*"
    *bing bing bong*

    All hail the wizards of Roku, the great and powerful ! ๐Ÿ˜‰