def combine_in_place(first, second):
combined = []
maxLength = max(len(first), len(second))
for i in range(maxLength):
if len(first) < i+1:
combined.append(second[i])
elif len(second) < i+1:
combined.append(first[i])
else:
if not first[i]:
combined.append(second[i])
elif not second[i]:
combined.append(first[i])
else:
combined.append(first[i] + second[i])
return combined
print combine_in_place([['f'], ['j', 't'], [], ['s'], [], []], [['h', 'd'], [], [], []])
# prints: [['f', 'h', 'd'], ['d', 't'], [], ['s'], [], []]
So I know to program (been programming in Java for some time now). I am trying to learn Python but I am not sure what's the best way to do some stuff. My question is:
How to write the function in a more Pythonic way?
3 Answers 3
I would write something like that:
def combine(first, second):
ret_list = []
for i, j in zip(first, second):
add = []
if i is not None:
add.extend(i)
if j is not None:
add.extend(j)
ret_list.append(add)
return ret_list
I don't know if it is the most python-ish way but it is ok I think...
I mean you can also write something like this:
def combine(first, second):
return [(i.extend(j) if not (i is None or j is None) else i if j is None else j) for i, j in zip(first, second)]
But you cannot read and debug that. So I would strongly recommend against it.
Btw: Did not test the last one so I hope I did not forget a closing bracket ;)
Edit:
In order to keep all elements you have to add something to the end. The zip() function stops iterating after the shortest element reached its end. This is why the following method is needed instead of zip:
def combine(first, second):
ret_list = []
for i, j in itertools.zip_longest(first, second):
add = []
if i is not None:
add.extend(i)
if j is not None:
add.extend(j)
ret_list.append(add)
return ret_list
Note: Itertools simply must be imported first. (For Python2 it is itertools.izip_longest()
...)
-
\$\begingroup\$ This works but I want to keep empty lists at the end as well (which I did not make clear in my question). I edited my question to include that request. \$\endgroup\$Shehab Ellithy– Shehab Ellithy2020年01月06日 22:05:33 +00:00Commented Jan 6, 2020 at 22:05
-
\$\begingroup\$ When I tested the combine function, it removed the empty ones at the end. \$\endgroup\$Shehab Ellithy– Shehab Ellithy2020年01月06日 22:19:26 +00:00Commented Jan 6, 2020 at 22:19
-
\$\begingroup\$ @ShehabEllithy yeah forgot something. Wait a sec. \$\endgroup\$Cedric– Cedric2020年01月06日 22:20:43 +00:00Commented Jan 6, 2020 at 22:20
-
\$\begingroup\$ Perfect, that is what I needed. \$\endgroup\$Shehab Ellithy– Shehab Ellithy2020年01月07日 06:43:53 +00:00Commented Jan 7, 2020 at 6:43
Here's how I might do it:
from itertools import chain
from typing import List
def combine_in_place(*lists: List[List[str]]) -> List[List[str]]:
return [
list(chain(*[a[i] if i < len(a) else [] for a in lists]))
for i in range(max(len(a) for a in lists))
]
Some general principles I've attempted to follow:
- When you're operating on multiple things, try to express them as a single iterable rather than individual named variables
- If you can replace a bunch of individual
if x < y
with a single call tomin()
ormax()
, do that - It's generally preferable to build a list through comprehension rather than using
extend
/append
.
-
2\$\begingroup\$ Since you reached for
itertools
, I’d usezip_longest
with afillvalue
of[]
or()
. Then you can have the simpler[list(chain.from_iterable(elements)) for elements in zip_longest(*lists, fillvalue=[])]
\$\endgroup\$301_Moved_Permanently– 301_Moved_Permanently2020年01月07日 13:01:26 +00:00Commented Jan 7, 2020 at 13:01
The Pythonic way may include:
• know and follow your Python Enhancement Proposals and
• know and exploit your Python Standard Library ;
• follow sound development practices is debatably language oblivious
The code presented does not violate PEP 8 Style Guide for Python Code
It does not follow PEP 257 Docstring Conventions
or PEP 484 Type Hints
Documenting code, in Python using docstrings, is one of my pet programming peeves:
Note how implementations in answers follow the one in the question in not combining in place, neither in the inner lists, nor in the outer one.
Note also how _in_place
got omitted in Cedced_Bro's accepted answer.
No (previous) answer has suggested using/returning a generator or allowing an arbitrary number of lists, given the advantages, this, too, may be due to lacking documentation of combine_in_place()
.
I know to program
&combine_in_place([['f'], ['j', 't'], ...], [['h', 'd'], ...])
&# prints: [['f', 'h', 'd'], ['d', 't'], ...]
hm, hm, hm, hm. \$\endgroup\$in_place
mean in the name? Surely our definition differ as you are returning a new list. \$\endgroup\$itertools
way. It's justreturn map(chain.from_iterable, zip_longest(*args, fillvalue=()))
. \$\endgroup\$