I have a list of integers that I would like to convert to one number like:
numList = [1,2,3]
num = magic(numList)
print num, type(num)
>>> 123, <type 'int'>
What is the best way to implement the magic function?
Thanks for your help.
EDIT
I did find this, but it seems like there has to be a better way.
EDIT 2
Let's give some credit to Triptych and cdleary for their great answers! Thanks guys.
-
# Over-explaining a bit: def magic(numList): # [1,2,3] s = map(str, numList) # ['1','2','3'] s = ''.join(s) # '123' s = int(s) # 123 return s # How I'd probably write it: def magic(numList): s = ''.join(map(str, numList)) return int(s) # As a one-liner num = int(''.join(map(str,numList))) # Functionally: s = reduce(lambda x,y: x+str(y), numList, '') num = int(s) # Using some oft-forgotten built-ins: s = filter(str.isdigit, repr(numList)) num = int(s)TokenMacGuy : I had thought the map function was deprecated in favor of list comprehensions, but now I can no longer find a note to that effect. thank you, I'll be adding that back into my vocabulary.John Fouhy : I assume there's a bug in your "# How I'd probably write it:" and it should be `''.join(map(str, numList))` ? Also, for your "Cleverly" option, you need to int() the result.Robert Gould : Yes your missing map in #2. for a moment there I thought you really were doing magic!Triptych : haha thanks - I was editing a bunch and missed itAlabaster Codify : TokenMacGuy: you mean this? - http://www.artima.com/weblogs/viewpost.jsp?thread=98196 map, reduce, filter, lambda were all to go in 3k originallymdec : +1 for the different versionsdbr : +1 for the first two methods, the "functional" way seems a bit silly, and the filter/str.isdigit way seems like a horrible hack -
pseudo-code:
int magic(list nums) { int tot = 0 while (!nums.isEmpty()) { int digit = nums.takeFirst() tot *= 10 tot += digit } return tot }Dana : I think you missed the part where he was looking for a python solution :Pcdleary : That's okay -- Andrew's solution was actually one of the fastest when converted to Python. +1 from me!J.F. Sebastian : It is the fastest solution (@cdleary's implementation in Python) if the list size is less than 30. http://stackoverflow.com/questions/489999/python-convert-list-of-ints-to-one-number/493944#493944 -
def magic(numbers): return int(''.join([ "%d"%x for x in numbers]))elliot42 : nice to see someone else whose brain works like mine. -
Two solutions:
>>> nums = [1, 2, 3] >>> magic = lambda nums: int(''.join(str(i) for i in nums)) # Generator exp. >>> magic(nums) 123 >>> magic = lambda nums: sum(digit * 10 ** (len(nums) - 1 - i) # Summation ... for i, digit in enumerate(nums)) >>> magic(nums) 123The
map-oriented solution actually comes out ahead on my box -- you definitely should not usesumfor things that might be large numbers:
import collections import random import timeit import matplotlib.pyplot as pyplot MICROSECONDS_PER_SECOND = 1E6 FUNS = [] def test_fun(fun): FUNS.append(fun) return fun @test_fun def with_map(nums): return int(''.join(map(str, nums))) @test_fun def with_interpolation(nums): return int(''.join('%d' % num for num in nums)) @test_fun def with_genexp(nums): return int(''.join(str(num) for num in nums)) @test_fun def with_sum(nums): return sum(digit * 10 ** (len(nums) - 1 - i) for i, digit in enumerate(nums)) @test_fun def with_reduce(nums): return int(reduce(lambda x, y: x + str(y), nums, '')) @test_fun def with_builtins(nums): return int(filter(str.isdigit, repr(nums))) @test_fun def with_accumulator(nums): tot = 0 for num in nums: tot *= 10 tot += num return tot def time_test(digit_count, test_count=10000): """ :return: Map from func name to (normalized) microseconds per pass. """ print 'Digit count:', digit_count nums = [random.randrange(1, 10) for i in xrange(digit_count)] stmt = 'to_int(%r)' % nums result_by_method = {} for fun in FUNS: setup = 'from %s import %s as to_int' % (__name__, fun.func_name) t = timeit.Timer(stmt, setup) per_pass = t.timeit(number=test_count) / test_count per_pass *= MICROSECONDS_PER_SECOND print '%20s: %.2f usec/pass' % (fun.func_name, per_pass) result_by_method[fun.func_name] = per_pass return result_by_method if __name__ == '__main__': pass_times_by_method = collections.defaultdict(list) assert_results = [fun([1, 2, 3]) for fun in FUNS] assert all(result == 123 for result in assert_results) digit_counts = range(1, 100, 2) for digit_count in digit_counts: for method, result in time_test(digit_count).iteritems(): pass_times_by_method[method].append(result) for method, pass_times in pass_times_by_method.iteritems(): pyplot.plot(digit_counts, pass_times, label=method) pyplot.legend(loc='upper left') pyplot.xlabel('Number of Digits') pyplot.ylabel('Microseconds') pyplot.show()Casey : holy shit, thats awesome....thanks for doing that!cdleary : No problem, but remember you should probably use what's most readable unless you find it's a bottleneck. I just like timing things. ;-)J.F. Sebastian : I've measured performance of the above function. The results are slightly different e.g. with_accumulator() is faster for small `digit_count`. See http://stackoverflow.com/questions/489999/python-convert-list-of-ints-to-one-number/493944#493944 -
def magic(number): return int(''.join(str(i) for i in number))dbr : Nitpick - you can remove the `[ ]` and do the str'ing as a generation expression. `int(''.join(str(i) for i in number))` - it's.. two bytes quicker! -
This seems pretty clean, to me.
def magic( aList, base=10 ): n= 0 for d in aList: n = base*n + d return n -
This method works in 2.x as long as each element in the list is only a single digit. But you shouldn't actually use this. It's horrible.
>>> magic = lambda l:int(`l`[1::3]) >>> magic([3,1,3,3,7]) 31337 -
Using a generator expression:
def magic(numbers): digits = ''.join(str(n) for n in numbers) return int(digits)dbr : +1 for no strange usage of lambda/map/etc -
Just for completeness, here's a variant that uses
print()(works on Python 2.6-3.x):from __future__ import print_function try: from cStringIO import StringIO except ImportError: from io import StringIO def to_int(nums, _s = StringIO()): print(*nums, sep='', end='', file=_s) s = _s.getvalue() _s.truncate(0) return int(s)
I've measured performance of @cdleary's functions. The results are slightly different.
Each function tested with the input list generated by:
def randrange1_10(digit_count): # same as @cdleary return [random.randrange(1, 10) for i in xrange(digit_count)]You may supply your own function via
--sequence-creator=yourmodule.yourfunctioncommand-line argument (see below).The fastest functions for a given number of integers in a list (
len(nums) == digit_count) are:len(nums)in 1..30def _accumulator(nums): tot = 0 for num in nums: tot *= 10 tot += num return totlen(nums)in 30..1000def _map(nums): return int(''.join(map(str, nums))) def _imap(nums): return int(''.join(imap(str, nums)))
|------------------------------+-------------------| | Fitting polynom | Function | |------------------------------+-------------------| | 1.00 log2(N) + 1.25e-015 | N | | 2.00 log2(N) + 5.31e-018 | N*N | | 1.19 log2(N) + 1.116 | N*log2(N) | | 1.37 log2(N) + 2.232 | N*log2(N)*log2(N) | |------------------------------+-------------------| | 1.21 log2(N) + 0.063 | _interpolation | | 1.24 log2(N) - 0.610 | _genexp | | 1.25 log2(N) - 0.968 | _imap | | 1.30 log2(N) - 1.917 | _map |To plot the first figure download
cdleary.pyandmake-figures.pyand run (numpyandmatplotlibmust be installed to plot):$ python cdleary.pyOr
$ python make-figures.py --sort-function=cdleary._map \ > --sort-function=cdleary._imap \ > --sort-function=cdleary._interpolation \ > --sort-function=cdleary._genexp --sort-function=cdleary._sum \ > --sort-function=cdleary._reduce --sort-function=cdleary._builtins \ > --sort-function=cdleary._accumulator \ > --sequence-creator=cdleary.randrange1_10 --maxn=1000dbr : That's a strange way to write it in a Python 2.6/3.0 way.. `print(''.join(str(x) for x in [1,2,3,4,5]))` will work in Python 2.5, 2.6, 3.x, probably more...J.F. Sebastian : @dbr: the purpose was to use the print function. It is not recommended way, that's why I wrote "for completeness".

0 comments:
Post a Comment