// This is obviously not too right
",".splitBy("1,2,3,4,5")
// This should be right, because it reads out more naturally
"1,2,3,4,5".splitBy(",")
Seeing as Python uses the first version for .join() // This is obviously not too right
",".splitBy("1,2,3,4,5")
// This should be right, because it reads out more naturally
"1,2,3,4,5".splitBy(",")
Seeing as Python uses the first version for .join() Python 3.8.2 (default, Jul 16 2020, 14:00:26)
>>> " ".join(["a", "b"])
'a b'
vs Ruby 2.6.5 :001 > ["a", "b"].join(" ")
=> "a b"
I don't know which one is more natural but I prefer the Ruby version because it's consistent with "a b".split(" ")
which works in both languages. One less think to remember.["a", "b"].join(" ")
This would mean the type list has a method join, how would it work with the following ?
[1.5, "hello", None].join(" ")
>>> " ".join([1.5, "hello", None])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: sequence item 0: expected string, float found
Why would `[1.5, "hello", None].join(" ")` be any different?Why would you put a method on a class if only a small subset of it can use it?
>>> f"{1.5} {'hello'} {None}"
'1.5 hello None'
Ruby does that. From [1] "[Array#join] Returns a string created by converting each element of the array to a string, separated by the given separator."The difference is that nil (Ruby's None) disappears
2.6.5 :001 > [1.5, "hello", nil].join(" ")
=> "1.5 hello "
That's totally expected because in Ruby the conversion of nil into a string is the empty string. Python converts it into the string "None".[1] https://ruby-doc.org/core-2.6.5/Array.html#method-i-join
Edit: an example with a type that normally wouldn't meaningfully cast to string
2.6.5 :001 > class Example
2.6.5 :002?> attr_accessor :name
2.6.5 :003?> end
=> nil
2.6.5 :004 > e = Example.new
=> #<Example:0x000055a321fa2bb0>
2.6.5 :005 > e.name = "example"
=> "example"
2.6.5 :006 > e.to_s
=> "#<Example:0x000055a321fa2bb0>"
2.6.5 :007 > [1, e].join(" ")
=> "1 #<Example:0x000055a321fa2bb0>"
2.6.5 :008 > class Example
2.6.5 :009?> attr_accessor :name
2.6.5 :010?> def to_s
2.6.5 :011?> "#{name}"
2.6.5 :012?> end
2.6.5 :013?> end
=> :to_s
2.6.5 :014 > e = Example.new
=> #<Example:0x000055a321f8a4c0>
2.6.5 :015 > e.name = "example"
=> "example"
2.6.5 :016 > e.to_s
=> "example"
2.6.5 :017 > [1, e].join(" ")
=> "1 example"in python, `join` is implemented in the `string` class, and the argument is an iterable. therefore, if your class wants to support `join`, it needs to implement the `__iter__` method (python's equivalent of ruby's `each`), but it does not need to also mix in an implementation of `join`.
it's mostly a cultural difference between ruby and python - the ruby ecosystem leans more heavily towards mixins and implementing generic functionality by attaching methods to objects, whereas the python ecosystem leans more heavily towards implementing generic functionality by providing functions (or methods on an external class) that accept generic objects.
A class that wants to support join has to implement to_s. The definition of join is
> join(separator=$,) → str
> Returns a string created by converting each element of the array to a string, separated by the given separator. If the separator is nil, it uses current $,. If both the separator and $, are nil, it uses an empty string.
If the class doesn't implement to_s Object.to_s kicks in and displays something like "#<SomeClass:0x000055a321fa2bb0>"
Enumerable is really a mixin https://ruby-doc.org/core-2.6.5/Enumerable.html but join is not listed in its methods.
There is another reply of mine with an example of a class can be used in join and doesn't mix in Enumerable.
Consider this crude csv parser:
using splitBy:
csv.splitBy("\n").map(csv_row=>csv_row.splitBy(","))
using split: "\n".split(csv).map(",".split)
split was the clear winner here. Given splitBy, I basically recreated split as a lambda.I tried to create an analog to that where splitBy would come out looking better. I figured that if we didn't know the dimensionality of our data, then delimiters becomes an array of arbitrary length and we could pass data.!splitBy into something like delimiters.reduce. When actually writing that, however, I wound up recreating split again:
using splitBy:
delimiters.reduce((accum,delim)=>accum.deepMap(data=>data.splitBy(delim)),[data])
using split: delimiters.reduce((accum,delim)=>accum.deepMap(delim.split),[data])1 + "hello" == "1hello"
Therefore, the type Array can have a join method that have predictable results (unlike Python).
As mentioned above, it's because Python supports `join` on any sequence, not just the array class. Personally, I find JavaScript's solution neater:
[..."hello"].join(" ") === "h e l l o"Javascript always falls back to string, that is also why we have === and == operators.
Python's type system is a bit more strict because it will not attempt to serialize your objet if you haven't explicitly done it yourself.
My point is: Python doesn't use this notation because it would not make sense in Python.
>>> [1,2,10].sort()
Array(3) [ 1, 10, 2 ]