Any other more elegant way than list comprehension?

x = [1,-1,-1,1]
color = ['red' if i == 1 else 'gree' for i in x]
color
['red', 'green', 'green', 'red']

I feel list comprehension is a bit of ugly,any other more elegant way

Except for the missing “n” it looks good / normal.

Of course you can use some trickery instead, for example:

color = [(None, 'red', 'green')[i] for i in x]
1 Like

I was going to suggest a dictionary rather than that indexing wrap-around trick; but yes.

IMHO, I think your code is elegant.

“ugly”, “elegant” are subjective terms…
(not measurable… sometimes even meaningless!!)

Someone says it’s ugly at the exact moment that someone else says it’s elegant
even among Python intermediates. (not me. I’m novice and duck.)
(I assume or hope that there is a kind of consensus in group of “experts”)

Readability is also quite subjective but less controversal…
Your code are perfectly readable for me and looks totally okay.

What makes you feel in that way?

Others out of infinite ways…

x = [1, -1, -1, 1]
red_or_green = {
    1: "red",
    -1: "green",
}
color = [red_or_green[i] for i in x]
x = [1, -1, -1, 1]

def red_or_green(number):
    assert number in [1, -1]
    if number == 1:
        return "red"
    return "green"

color = [red_or_green(i) for i in x]
color = list(map(red_or_green, x))
1 Like

Meh, that felt too normal / boring :-P. But ok:

color = [*map({1: 'red', -1: 'green'}.get, x)]
3 Likes

Yet another:

color = ['red' * i or 'green' for i in x]
1 Like

I agree. That is pretty ugly. But I think it’s the ternary operator that makes it ugly. I never liked Python’s ternary operator.

I prefer a more general solution that allows an arbitrary True/False expression.

This should allow any expression that evaluates to a boolean. Does it look a little less ugly to you?:

[["green", "red"][int(i == 1)] for i in x]

I have mixed feelings about it. At least most people will understand your intent with the ternary.

I’d make that [("green", "red")[i == 1] for i in x]. Creating a new list for each element and calling int just seems wasteful.

ISTR that the ternary operator is deliberately cumbersome because the
language authors were reluctant to have one at all.

My personal criterion for beautiful vs ugly is roughly: is is easy to
read? That is also subjective, particularly for an idiom with which I’m
unfamiliar (since knowing a particular idiom makes that particular thing
recognisable), but it is still a workable approach. As the Zen says,
“readility counts”.

So I’ll take the list comprehension with the ternary because I can read
it directly; some indentation may help there too.

Whereas something where we compute some feature then “do some math using
the feature” to extract the result is less readable to me because I have
to figure out the math. To take your example:

 [["green", "red"][int(i == 1)] for i in x]

this is pretty good except for the math: I need to figure out that you
can int() a Boolean (basicly because bools are an int subclass for
historic reasons) and then infer the 0/1 relationshit to the green/red
pair. That’s “do some math”, not as direct.

So I’d still prefer:

 "red" if i == 1 else "green"

because it does directly what it says on the tin.

There are always many increasingly complex ways to write things, but
this isn’t Perl and we’re not playing JAPH (where Perlistas stick Perl
one-liners in their sig quote which print “Just another Perl hacker.” in
via some obtuse mechanism, the obtuser the better).

Cheers,
Cameron Simpson cs@cskk.id.au
a reformed Perl programmer

Yes, sometimes Perl looks like line-noise to the uninitiated, but to the
seasoned Perl programmer, it looks like checksummed line-noise with a mission
in life. - The Llama Book

1 Like

I think the most readable is the first example from @capymind: define a dict and then the list comprehension is simple. It’s more lines of code, but lines of code are cheap as long as they’re easy to read. It’s also efficient and easily extensible (additional colors, different colors, etc).

I would use the dict to map from the int value to the string value for two reasons.
I consider it obvious what the conversion is.
If the input data contains a new value then the code with traceback and can be fixed.
A long way down reasons to use dict is that adding a new code say 0 means “amber” is simple to add.

2 Likes

The original post gave us some code, but did not supply us with a problem description, therefore we have needed to make some assumptions about what needs to be done here. If the hypothetical problem description did require a list comprehension and the given palette with only two colors, then the list comprehension with the ternary operator used in the original post is fine.

However, if a list comprehension and unvarying palette are not required by the problem description, then we are free to take a different approach. We could generalize the solution by using a function to enable the option of using a variety of color palettes, as in the following:

def color_sequence(palette, codes):
    return [*map(palette.get, codes)]

mondays_palette = {-1: "green", 1: "red"}
mondays_keys = [1, -1, -1, 1]

tuesdays_palette = {4: "green", 7: "quercitron", 10: "red"}
tuesdays_keys = [10, 4, 7, 10, 7]

print(color_sequence(mondays_palette, mondays_keys))
print(color_sequence(tuesdays_palette, tuesdays_keys))

Output:

['red', 'green', 'green', 'red']
['red', 'green', 'quercitron', 'red', 'quercitron']