2016-05-31 1 views
2

Gibt es eine kanonische Methode, mehrere Schlüssel von einem einzigen Element in der Eingabesequenz auszugeben, so dass sie eine fortlaufende Sequenz bilden und ich keine verwenden muss nur um die Sequenz zu glätten?python map/reduce: mehrere Schlüsselwerte aus einer einzelnen Map ausgeben lambda

z.B. wenn ich jede Ziffer in einer Reihe von Zahlen in einzelne Zahlen in einer Folge

[1,12,123,1234,12345] => [1,1,2,1,2,3,1,2,3,4,1,2,3,4,5] 

dann würde ich einige Python schreiben, sah ein bisschen so erweitern wollte:

somedata = [1,12,123,1234,12345] 

listified = map(lambda x:[int(c) for c in str(x)], somedata) 
flattened = reduce(lambda x,y: x+y,listified,[]) 

würde aber lieber nicht die flattened = reduce(...) anrufen müssen, wenn es eine bessere (oder vielleicht effizientere) Möglichkeit gibt, dies auszudrücken.

Antwort

3

map(func, *iterables) wird immer func so oft wie die Länge der kürzesten iterablen (vorausgesetzt, keine Ausnahme ausgelöst wird). Funktionen geben immer ein einzelnes Objekt zurück. So wird list(map(func, *iterables)) immer die gleiche Länge wie die kürzeste iterable haben.

Somit hat list(map(lambda x:[int(c) for c in str(x)], somedata)) immer die gleiche Länge wie somedata. Es gibt keinen Weg dahin.

Wenn das gewünschte Ergebnis (z. B. [1,1,2,1,2,3,1,2,3,4,1,2,3,4,5]) mehr Elemente als die Eingabe aufweist (z. B. [1,12,123,1234,12345]), muss etwas anderes als map verwendet werden, um es zu erzeugen.

Sie könnten zum Beispiel itertools.chain.from_iterable 2 Verschachtelungsebenen zu glätten verwenden:

In [31]: import itertools as IT 

In [32]: somedata = [1,12,123,1234,12345] 

In [33]: list(map(int, IT.chain.from_iterable(map(str, somedata)))) 
Out[33]: [1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5] 

oder eine Liste von Listen zu glätten, sum(..., []) genügt:

In [44]: sum(map(lambda x:[int(c) for c in str(x)], somedata), []) 
Out[44]: [1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5] 

aber beachten Sie, dass dies viel langsamer als mit IT.chain.from_iterable (siehe unten).


Hier ist ein Benchmark-Tests die verschiedenen Methoden auf einer Liste von 10.000 Zahlen von 0 bis eine Million (IPython ‚s %timeit verwenden):

In [4]: import random 
In [8]: import functools 
In [49]: somedata = [random.randint(0, 10**6) for i in range(10**4)] 

In [50]: %timeit list(map(int, IT.chain.from_iterable(map(str, somedata)))) 
100 loops, best of 3: 9.35 ms per loop 

In [13]: %timeit [int(i) for i in list(''.join(str(somedata)[1:-1].replace(', ','')))] 
100 loops, best of 3: 12.2 ms per loop 

In [52]: %timeit [int(j) for i in somedata for j in str(i)] 
100 loops, best of 3: 12.3 ms per loop 

In [51]: %timeit sum(map(lambda x:[int(c) for c in str(x)], somedata), []) 
1 loop, best of 3: 869 ms per loop 

In [9]: %timeit listified = map(lambda x:[int(c) for c in str(x)], somedata); functools.reduce(lambda x,y: x+y,listified,[]) 
1 loop, best of 3: 871 ms per loop 
2

Got zwei Ideen, ein mit Liste comprehentions:

print [int(j) for i in somedata for j in list(str(i)) ] 

Etwas neues (aus den Kommentaren), ist Zeichenfolge bereits iterable, soll es so sein würde:

print [int(j) for i in somedata for j in str(i) ] 

Sekunde mit opertations auf Strings und Liste comprehentions:

print [int(i) for i in list(''.join(str(somedata)[1:-1].replace(', ','')))] 

Ausgang für beide:

[1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5] 
+0

Sie nicht brauchen 'list' zu rufen' str'. 'str (i)' ist bereits iterierbar. –

Verwandte Themen