0

I've started using/learning list-comprehensions, as an exercise I decided to use the following code:

def data_idx(data):
    if data <= 15:
        n = ''
    elif data <= 31:
        n = 2
    elif data <= 47: 
        n = 3
    elif data <= 63: 
        n = 4
    else:  
        n = 5
    return n

I'm wondering, if it is possible to simplify the above code to one line?

I've tried the following approach:

data = 63 
limits = [31, 47, 63, 100]
if data > 15:
    x = [n+2 for n in range(len(limits)) if limits[n] <= data < limits[n+1]][0]
else:
    x = ''

But I don't know how to deal with values <= 15, to make an output ''.

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
Gооd_Mаn
  • 343
  • 1
  • 16
  • your code is not equivalent teach other. – RomainL. Nov 26 '20 at 14:24
  • https://docs.python.org/3/library/bisect.html should be helpful – Serial Lazer Nov 26 '20 at 14:24
  • @Djib2011, hmmm... I wonder why this line of code works in python3.7... x = [n+2 for n in range(len(limits)) if limits[n] <= data < limits[n+1]][0] – Gооd_Mаn Nov 26 '20 at 14:50
  • 1
    @Djib2011 `A < B < C` is permitted in Python. – Sachin Raja Nov 26 '20 at 14:52
  • Yeah you're right, I'll delete my comment – Djib2011 Nov 26 '20 at 14:54
  • 2
    List comprehensions shouldn't be used for side effects. As I see it, that's a side-effect: you are creating the list just to take the first element. To be honest, you shouldn't change your code. It is clear, readable and efficient enough. Anything else will just make the reader of your code scratch their forehead... – Tomerikoo Nov 26 '20 at 15:33

3 Answers3

2

Usually in Python, the simplification of a if/elif/else structure is done by using a dict. For example:

if n == 2:
    x = 'two'
elif n == 3:
    x = 'three'
else:
    x = 'na'

Can be simplified into:

x = {2: 'two', 3: 'three'}.get(n, 'na')

But since you're comparing and not checking equality, this gets trickier. You are also not using fixed ranges so can't use a switch-like dict with ranges.

Still, to emulate your ifs structure, you can create a matching of limits and their relevant symbol and find the next one that matches using a generator expression (rather than a list-comprehension):

def data_idx(data):
    limits  = [15, 31, 47, 63]
    symbols = ['',  2,  3,  4]

    return next((symbol for limit, symbol in zip(limits, symbols) if data <= limit), 5)

This simply checks in what range the data falls into and returns the matching symbol. If none matched (so data is greater than 63), it will return 5 (the default for next).

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
1

Disclaimer: I don't recommend you use this for anything that you or someone else will have to read in the future. There are better, clearer solutions that don't use list comprehension to accomplish this.

But you asked for a one-liner using list comprehension...

data = 63
limits = [0, 15, 31, 47, 63]

n = [idx if idx > 1 else '' for idx, limit in enumerate(limits, 1) if data > limit][-1]
big_bad_bison
  • 1,021
  • 1
  • 7
  • 12
1
def data_idx(data):
    return [repr('') if i <= 15 else 2 if i <= 31 else 3 if i <= 47 else 4 if i <= 63 else 5 for i in (data, )][0]

limits = [15, 31, 47, 63, 100]

for i in limits:
    print(data_idx(i))
''
2
3
4
5
Henry Tjhia
  • 742
  • 1
  • 5
  • 11
  • 2
    While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value. – Donald Duck Nov 26 '20 at 20:40
  • Why do you even use list-comp here? You can just replace `i` with `data` and return the expression of the list-comp... No reason to create a list with element and take the first element - just return that element... – Tomerikoo Nov 29 '20 at 08:04