Today I worked on a challenge on Codewars and saw a very concise solution submitted by another user on the site. I'll first just show the code, then explain what's going on.
def up_array(arr):
if not arr or min(arr) < 0 or max(arr) > 9:
return None
else:
return [int(y) for y in str(int("".join([str(x) for x in arr])) + 1)]
The challenge can be found here, but the basic idea is to take a list of integers, add one to the value represented by that list, and return a new list with that new value.
Let's break down that solution.
First, there's an if statement with multiple parts. if
starts it off. Then there is the expression not arr
. This is nice. It takes advantage of the fact that data types like list can have a boolean interpretation. In this case, if the array is empty, not arr
will evaluate to True
(because arr
would evaluate to False
).
This is attached to the next bit by or
. The next two parts are more straightforward. If the minimum of the array is less than zero or if the maximum of the array is greater than nine, the if statement will evaluate to True
.
So, if the list is empty or contains elements out of range, the function will return None
.
Now the real meat of it.
return [int(y) for y in str(int("".join([str(x) for x in arr])) + 1)]
Wheh! That's a mouthful.
At the center of the parenthesis and brackets, we have: str(x) for x in arr
. That seems straightforward enough. It seems like this would be functionally equivalent to the following (which is pseudo code):
y = []
for x in arr:
y = str(x)
The point is, I think that str(x) for x in arr
would return an array of strings which have been converted from the integers in arr.
Let's test this.
In arr = [2,3,9]
In [str(x) for x in arr]
Out ['2', '3', '9']
Yes. That's how it works.
Next step: "".join([str(x) for x in arr])
. It looks like we are joining an empty string to the array of strings. Or maybe we are joining the array of strings into one array with the empty string between them. I can test this.
In[4]: [str(x) for x in arr]
Out[4]: ['2', '3', '9']
In[5]: "".join([str(x) for x in arr])
Out[5]: '239'
In[6] "-".join([str(x) for x in arr])
Out[6]: '2-3-9'
Yes. It joins together the strings of the array, with either nothing ("") or a dash ("-") between the strings.
What next?
We have (int("".join([str(x) for x in arr])) + 1)
. This is pretty straight forward. Take the result from above, convert it to an integer, then add one. Cool.
Next, we have str(int("".join([str(x) for x in arr])) + 1)
. This is an easy step. We're just taking the resulting integer and converting it to a string.
This next bit is a bit more tricky. [int(y) for y in str(int("".join([str(x) for x in arr])) + 1)]
. I believe this does something analogous to:
new_list = []
for y in my_string:
y = int(y)
new_list.append(y)
So it should take each character from the string, convert that to an integer, and put it into a list. Let's test it.
In[7]: [int(y) for y in str(int("".join([str(x) for x in arr])) + 1)]
Out[7]: [2, 4, 0]
Yes. that's what it does. Good.
Then there's just one more step: return [int(y) for y in str(int("".join([str(x) for x in arr])) + 1)]
. That means take the result of all that code, and return it as the function output.
Summary
So, to summarize that code, we have:
def up_array(arr):
if not arr or min(arr) < 0 or max(arr) > 9:
return None
else:
return [int(y) for y in str(int("".join([str(x) for x in arr])) + 1)]
Which, if I translate to pseudo code, would look something like:
define function up_array as a function of the array, arr:
if there are problems with the input:
return nothing
otherwise:
take a list of integers, convert each to a string and put them
together into a string, convert that string to an int, add one to
that int, and convert that result to a string. Then pull out the
characters of that string one by one, converting each into a
string and populating a list with those strings.
Return that list.
I learned a lot from picking this apart, and I hope you learned something from reading it.
Top comments (3)
Nested list comprehensions, nice :)
I wonder if the casts between int and str are necessary though.. perhaps there is a solution involving a more traditional add and carry approach?
Yeah, I thought it was a neat solution. :)
I'm not familiar with add and carry approaches, but it's interesting to consider whether it's possible without the casts.
I got bored and had a go. Please do not look at my code.....:)