Understand "better" as a quicker, elegant and readable.
I have two strings (a
and b
) that could be null or not. And I want concatenate them separated by a hyphen only if both are not null:
a - b
a
(if b is null)
b
(where a is null)
Understand "better" as a quicker, elegant and readable.
I have two strings (a
and b
) that could be null or not. And I want concatenate them separated by a hyphen only if both are not null:
a - b
a
(if b is null)
b
(where a is null)
# Concatenates a and b with ' - ' or Coalesces them if one is None
'-'.join([x for x in (a,b) if x])
Edit
Here are the results of this algorithm (Note that None will work the same as ''):
>>> '-'.join([x for x in ('foo','bar') if x])
'foo-bar'
>>> '-'.join([x for x in ('foo','') if x])
'foo'
>>> '-'.join([x for x in ('','bar') if x])
'bar'
>>> '-'.join([x for x in ('','') if x])
''
*Also note that Rafael's assessment, in his post below, only showed a difference of .0002 secs over a 1000 iterations of the filter method, it can be reasoned that such a small difference can be due to inconsistencies in available system resources at the time of running the script. I ran his timeit implementation over several iteration and found that either algorithm will be faster about 50% of the time, neither by a wide margin. Thus showing they are basically equivalent.
tuple
in the inner list (a,b)
rather than [a,b]
) –
Catchweight join()
here, which I am against: when I personally see join
I expect either a bigger-than-two or variable(!) amount of items to be joined. I would never use join()
to join two strings explicitly. –
Kilometer '-'
. It's pretty idiomatic to leave them off -- In fact, I've seen it written with parenthesis so rarely that the parenthesis actually made me take a second look at this. –
Catchweight join
directly: '-'.join(x for x in (a,b) if x)
. This should run (slightly) faster and use (slightly) less memory, because you're not building an intermediate list and then throwing it away. It also looks nice on the page. –
Ravage join
goes over the list twice because it pre-calculates the memory required before actually doing the join. –
Phore for x in [a, b]
. The line that separates tuples from lists in Python is a bit blurry (because tuples take so many methods you'd expect only in lists), but conceptually this makes more sense as a "list of strings" than a "tuple of strings". 2) It's been explained a zillion times why "join" is a method of string and not from lists/iterables/whatever, but yet, it's a pitty we can't write things like strings.compact.join("-")
like they do in, well, other languages. –
Dementia How about something simple like:
# if I always need a string even when `a` and `b` are both null,
# I would set `output` to a default beforehand.
# Or actually, as Supr points out, simply do `a or b or 'default'`
if a and b:
output = '%s - %s' % (a, b)
else:
output = a or b
Edit: Lots of interesting solutions in this thread. I chose this solution because I was emphasizing readability and quickness, at least in terms of implementation. It's not the most scalable or interesting solution, but for this scope it works, and lets me move on to the next problem very quickly.
output = a or b or 'default'
instead of setting it beforehand? –
Lindsley Wow, seems like a hot question :p My proposal:
' - '.join(filter(bool, (a, b)))
Which gives:
>>> ' - '.join(filter(bool, ('', '')))
''
>>> ' - '.join(filter(bool, ('1', '')))
'1'
>>> ' - '.join(filter(bool, ('1', '2')))
'1 - 2'
>>> ' - '.join(filter(bool, ('', '2')))
'2'
Obviously, None
behaves like ''
with this code.
filter(None,(a,b))
would also work, but bool
is probably a little more explicit. For the record, this really isn't any different than the version above which uses a list-comp instead. –
Catchweight bool
I get a slight decrease in performance. Probably because passing None
allows to call the truth methods more directly, while passing bool
the filter
has to do a normal function call. –
Coursing Here is one option:
("%s - %s" if (a and b) else "%s%s") % (a,b)
EDIT: As pointed by mgilson, this code would fail on with None
's a better way (but less readable one) would be:
"%s - %s" % (a,b) if (a and b) else (a or b)
a = None
case if you re-write it slightly: "%s - %s"%(a,b) if (a and b) else (a or b)
-- But that's harder to read I would say. –
Catchweight " - ".join((a, b)) if a and b else a or b
–
Anechoic I just wanted to offer toxotes' solution rewritten as a one liner using format
.
output = "{0} - {1}".format(a, b) if (a and b) else (a or b)
There's a lot of answers here :)
The two best answers (performance and clean code in one line) are the answers of @icecrime and @Hoopdady
Both asnwers results equally, the only difference is performance.
cases = [
(None, 'testB'),
('', 'testB'),
('testA', 'testB'),
('testA', ''),
('testA', None),
(None, None)
]
for case in cases: print '-'.join(filter(bool, case))
'testB'
'testB'
'testA-testB'
'testA'
'testA'
for case in cases: print '-'.join([x for x in case if x])
'testB'
'testB'
'testA-testB'
'testA'
'testA'
So let's do a benchmark :)
import timeit
setup = '''
cases = [
(None, "testB"),
("", "testB"),
("testA","testB"),
("testA", ""),
("testA", None),
(None, None)
]
'''
print min(timeit.Timer(
"for case in cases: '-'.join([x for x in case if x])", setup=setup
).repeat(5, 1000))
0.00171494483948
print min(timeit.Timer(
"for case in cases: '-'.join(filter(bool, case))", setup=setup
).repeat(5, 1000))
0.00283288955688
But, as @mgilson said, using None
instead of bool
as the function in filter
produces the same result and have a quite better performance:
print min(timeit.Timer(
"for case in cases: '-'.join(filter(None, case))", setup=setup
).repeat(5, 1000))
0.00154685974121
So, the best result is the answer gave by @icecrime with the suggestion from @mgilson:
'-'.join(filter(None, (a,b)))
The performance difference is in milliseconds per 1000 iterations (microseconds per iteration). So these two methods have a quite equals performance, and, for almost any project you could choose any one; In case your project must have a better performance, considering microseconds, you could follow this benchmark :)
setup
declaration and I've missed a )
in your code, fixed now! –
Eberhard Do it like this:
'-'.join(max(x,'') for x in [a,b] if x is not None)
Try this:
def myfunc(a,b):
if not b:
return a
elif not a:
return b
else:
return a+' - '+b
Or
def myfunc(a,b):
if a and b:
return a+' - '+b
else:
return a or b
else
if the preceding if
will return from the function. I often wondered if it should be included for readability, but decided against it. It'd be nice if we could do return a if b else c
. –
Alleviation return a if b else c
–
Informal return (a if b else c)
–
Informal Something pythonian, readable and elegant:
strings = string1, string2
'{0}{1}{2}'.format(
# output first string if it's not empty
strings[0] if strings[0] else '',
# join with hyphen if both strings are not empty
'-' if all(strings) else '',
# output second string if it's not empty
strings[1] if strings[1] else ''
)
And fast too ;)
I would do it like this:
def show_together(item1=None, item2=None, seperator='-'):
return '%s%s%s' % (item1,seperator,item2) if item1 and item2 else item1 or item2
>>> show_together(1,1)
'1-1'
>>> show_together(1)
1
>>> show_together()
>>> show_together(4,4,'$')
'4$4'
© 2022 - 2024 — McMap. All rights reserved.