13

In Python, you can call the string.join() method on any iterable, like so:

",".join(some_iterable)

The argument may be a list, generator, or any other object as long as it is iterable.

Playing around with ES6, I couldn't find a way to do so without having to create an array first, I had to do something like this:

function *myGenerator() { ... }
let output = [...myGenerator()].join(",");

I know that join() is an Array.prototype method. Is it possible for me to call join() or some equivalent to concatenate the values generated by myGenerator without having to create an intermediate array, like the python example above?

Nasser Al-Shawwa
  • 3,573
  • 17
  • 27
  • 2
    You can just add a function that implements that to `String.prototype` as `join`, if you want to; then you *could* call `','.join(myGenerator())`. But you'll at some point either have to create an array to use *that* join method, or write your own equivalent. And under the hood, I believe the CPython equivalent *does* create an intermediate array, as that's more efficient than creating lots of intermediate strings instead. – jonrsharpe Jul 09 '18 at 22:54
  • @jonrsharpe I believe that, in theory, you don't need an intermediate array. If what I'm asking is not achievable at present, then this seems like an overlooked feature of the generators implementation, no? – Nasser Al-Shawwa Jul 09 '18 at 23:01
  • 1
    No, you don't *need* the array, you could perfectly well write a function that just consumes the iterator and builds the equivalent string. But that builds lots of intermediate *strings* instead, which is likely less efficient overall. And either way, if that's what you want to write, what is stopping you writing it? – jonrsharpe Jul 09 '18 at 23:02
  • An answer to your *literal* question and not the *spirit* of your question is, you could have a function that takes an iterator, iterates over it, concatenating with a string while adding separators. – CertainPerformance Jul 09 '18 at 23:03
  • @CertainPerformance I considered this ofcourse, but you're right in your comment in that that's not quite what I'm looking for. – Nasser Al-Shawwa Jul 09 '18 at 23:05
  • 2
    @jonrsharpe You're right, nothing is stopping me from writing it myself, I'm just curious to know if there's an ES6 feature that allows me to join on generators just like I can with `Array.prototype.join()`. – Nasser Al-Shawwa Jul 09 '18 at 23:08
  • @nasser-sh I'm a bit confused on your question. What value(s) is your generator returning? – Geuis Jul 09 '18 at 23:35
  • @Geuis the generator is yielding strings that I'd like to concatenate – Nasser Al-Shawwa Jul 09 '18 at 23:39
  • @nasser-sh You should post a full example of your code (what's actually in the generator) because its not clear right now. – Geuis Jul 10 '18 at 00:21

1 Answers1

7

The comments above answer your question pretty well. However, I was curious about @jonrsharpe's comment about generating intermediate strings and was wondering how that actually affected performance. So I put a join method on the prototype of a generator and tested it.

Here's the join() code:

function* nGen(start, stop) {
  while (start < stop) {
    yield start
    start++
  }
}

nGen.prototype.join = function(sep) {
  let res = this.next().value
  for (let v = this.next(); !v.done; v = this.next()) {
    res += sep + v.value
  }
  return res
}


let g = nGen(2, 20)

console.log(g.join(','))

The original jsPerf tests in Safari and Chrome showed this working faster than the very common idiom: [...g].join(','). In a JSBench test in 2022, results were inconsistent between Chrome, Firefox, and Edge, but this method is now slower than [...g].join(',') and/or a for loop.

I am far from a master of writing jsPerf/JSBench tests, so maybe I'm screwing something up or misinterpreting the result. But if you're interested: https://jsbench.me/zlkz8tm6vw/1

Jacktose
  • 709
  • 7
  • 21
Mark
  • 90,562
  • 7
  • 108
  • 148