How to convert a loop to a list in Python - a specific puzzle
This code, if you give it the number N, generates 1234 ... N or N ... 4321, but outputs the result not as a string, but rather as a number. I wanted to convert the loop in the middle to a list comprehension but ran into difficulty trying to do this.
Here's the code that works (written in Python 2.7:
def findTens(N):
# counts number of tens in a number: examples:
# * 100 & 113 would both return 2
# * 9 returns 0
# * 14 returns 1
incrementer = 1
while True:
if N - 10**incrementer < 0:
break
else:
incrementer += 1
if incrementer == 100:
break # debugging condition
return incrementer - 1
def create_seqNum(N, reverse=False, showWork=False, returnDescr=False, divLength=100):
'''create_seqNum() --> iput N, and get back a number built from the sequence of 1234...N
Arguments: reverse=True to get the sequence in revers, showWork=True to see numbers that add up to final,
returnDescr=True to print the answer in a sentence as well as returning it as a number.'''
num = 0
tensIncr = 0
answer = 0
Ntens = findTens(N)
modifier = 0 # modifies counter when increment of 10 occurs
if reverse == True: # create range builder inputs
rstart = 1
rend = N+1
rinc = 1
else:
rstart = N
rend = 0
rinc = -1
for i in range(rstart, rend, rinc):
itens = findTens(i)
num = i * 10**tensIncr
tensIncr += 1 + itens
pad = (Ntens - itens)
if showWork == True:
print(("For %d" + " "*pad + " Add: %d") %(i, num))
answer += num
if showWork == True:
print("#"*divLength)
if showWork == True or returnDescr == True:
print("Answer: %d" %answer)
print("#"*divLength)
return answer
All of these test cases work with the above code:
for i in [1, 5, 9, 10, 11, 13, 98, 99, 100, 101, 102, 107, 1012]:
create_seqNum(i, reverse=True, returnDescr=True)
create_seqNum(i, returnDescr=True)
Note also that my attempt to create a worklist comprehension broke both to break out of the loop in the calculation and to get a list of added numbers as well as the grand total ("showWork" option), but if no one breaks them then the best decision will be made for a calculation that will pass all tests.
In case it helps - here is my attempt at converting it to a list of issues that failed. If someone can figure this out by trying to find out from your answer and thought others might find the puzzle of this interesting (at least I hope):
def create_seqNum_v2(N, reverse=False, showWork=False, returnDescr=False, divLength=100):
'''create_seqNum() --> iput N, and get back a number built from the sequence of 1234...N
Arguments: reverse=True to get the sequence in revers, showWork=True to see numbers that add up to final,
returnDescr=True to print the answer in a sentence as well as returning it as a number.'''
num = 0
tensIncr = 0
answer = 0
Ntens = findTens(N)
modifier = 0 # modifies counter when increment of 10 occurs
if reverse == True: # create range builder inputs
rstart = 1
rend = N+1
rinc = 1
else:
rstart = N
rend = 0
rinc = -1
workList = [i * 10**((i-1) + findTens(i)) for i in range(rstart, rend, rinc)]
answer = sum(workList)
if showWork == True:
# [ print(("For %d" + " "*(Ntens - findTens(i)) + " Add: %s") %(workList[i], i)) for i in workList ]
# [x for ind, x in enumerate(list1) if 4 > ind > 0]
[print(("%d" + " "*(Ntens-findTens(x)) + ": %s") %(ind, x)) for ind, x in enumerate(workList)]
if showWork == True:
print("#"*divLength)
if showWork == True or returnDescr == True:
print("Answer: %d" %answer)
print("#"*divLength)
return answer
Source and background of this problem:
In HackerRank, you have an N = 123 ... N solution, just not using strings, and they take a simple one-line print format operator as the answer. While using python 3.x print arguments in connection with list unpacking is much more efficient for solving this problem, I wondered if it was possible to plot a number as a number without any string conversions to do it. Since print()
I think it is converted to a string under the covers before it outputs content, I felt this was a more interesting approach, from a purely academic point of view.
source to share
It can be done in one line (minus imports), in no more than O (n) steps, without writing separate functions or loops, just a standard map shorthand.
import math # to get math.log10
listofints = [1,2,3,10,12,19,99,100,101,50102030]
n = reduce(lambda x,y:[x[0]*(10**(y[1]+1))+y[0],0],map(lambda x:[x,int(math.log10(x))], listofints))[0]
print(n)
# the number 1231012199910010150102030
This uses the standard map-reduce method to decorate numbers using a map and then flattens them back to a number.
First step:
map(lambda x:[x,int(math.log10(x))], ...
accepts a list of integers such as:
[1,2,3,10,12,19,99,100,101,50102030]
and converts them to a list of pairs that are a number plus the base-10 logarithm:
[[1, 0], [2, 0], [3, 0], [10, 1], [12, 1], [19, 1], [99, 1], [100, 2], [101, 2], [50102030, 7]]
Then the next step is to reduce this list to one number:
reduce(lambda x,y:[x[0]*(10**(y[1]+1))+y[0],0],...
To do this, you need to multiply it by ten to make room for the next number to be added. Fortunately, the next number tells you how big it is. So multiply the pair by one number (first part) by 10 by (one plus) by the cardinality of the next pair (second part), and then add the second number of the pair (first part).
The reduction looks like this:
[[1, 0], [2, 0], [3, 0], [10, 1], [12, 1], [19, 1], [99, 1], [100, 2], [101, 2], [50102030, 7]]
[[12, 0], [3, 0], [10, 1], [12, 1], [19, 1], [99, 1], [100, 2], [101, 2], [50102030, 7]]
[[123, 0], [10, 1], [12, 1], [19, 1], [99, 1], [100, 2], [101, 2], [50102030, 7]]
[[12310, 0], [12, 1], [19, 1], [99, 1], [100, 2], [101, 2], [50102030, 7]]
[[1231012, 0], [19, 1], [99, 1], [100, 2], [101, 2], [50102030, 7]]
[[123101219, 0], [99, 1], [100, 2], [101, 2], [50102030, 7]]
[[12310121999, 0], [100, 2], [101, 2], [50102030, 7]]
[[12310121999100, 0], [101, 2], [50102030, 7]]
[[12310121999100101, 0], [50102030, 7]]
[[1231012199910010150102030, 0]]
Finally, the last element is [1231012199910010150102030, 0], so take its first element which is 1231012199910010150102030
UPDATE
While the whole job of reducing the number of cards is a good process to get used to, it was too much. This can be done without the entire map by simply using the shorthand:
n = reduce(lambda x,y:x*(10**(int(math.log10(y))+1))+y,listofints)
source to share
I'm not entirely sure I understand your problem, but are you looking for something like this?
def create_seqNum(num, reverse=False):
final_num = 0
x = [x for x in range(1, num+1)]
y = [y for y in range(num, 0, -1)]
for i in range(0, num):
print(x[i], y[i])
final_num += x[i]* 10**(y[i]-1)
return final_num
source to share