Home > Software design >  Python - switch from one list to another (optimal way)
Python - switch from one list to another (optimal way)

Time:03-31

I have two lists

list1 = [1, 3, 5, 8]
list2 = [7, 10, 12]

and some breaking int value switchValue = 4 I need to iterate through the list1 while its elements have value smaller than the switchValue, and after that to iterate through the full list2. I know how to do it with if and break statement in else branch but I was looking into some more optimal (recommended) way how to get this in Python 2.7, in regards to the performances since these lists will be quite large. Additionally- lists are sorted.

result should be 1, 3, 7, 10, 12

list1 = [1, 3, 5, 8]
list2 = [7, 10, 12]
switchValue = 4
for i in list1:
    if i < switchValue:
        print(i)
    else:
        for j in list2:
            print(j)
        break

CodePudding user response:

for i in list1:
    if i >= switchValue:
        break
    print(i)

for i in list2:
    print(i)

This is really all you want. Iterate through list1 until you find a specific value, then stop iterating through list1. Then iterate through the complete list2. These two things seem to be entirely separate actions, so there's no need to intertwine them in the same loop.

Note that this assumes that you always want to iterate list2, not only if and when you encountered switchValue. Your question isn't entirely clear on that point.

The first iteration can be dressed up with things like itertools.takewhile:

for i in takewhile(lambda i: i < switchValue, list1):
    print(i)

for i in list2:
    print(i)

To put both into the same loop, you can chain them:

for i in chain(takewhile(lambda i: i < switchValue, list1), list2):
    print(i)

CodePudding user response:

You could use binary search to find the switch index:

from bisect import bisect_left
switchIndex = bisect_left(list1, switchValue)

And then print without a Python loop (oops, doesn't work in Python 2, but the other one below does):

print(*list1[:switchIndex], sep='\n')
print(*list2, sep='\n')

Or:

print('\n'.join(map(str, list1[:switchIndex])))
print('\n'.join(map(str, list2)))

Benchmark along with yours and deceze's on 100,000 times longer lists in Python 2:

 51 ms   52 ms   52 ms  Kelly2
137 ms  138 ms  138 ms  original
138 ms  138 ms  138 ms  deceze1
160 ms  160 ms  161 ms  deceze2
164 ms  164 ms  165 ms  deceze3

In Python 3:

111 ms  114 ms  114 ms  Kelly2
134 ms  134 ms  135 ms  Kelly1
176 ms  177 ms  178 ms  deceze1
178 ms  178 ms  178 ms  original
189 ms  189 ms  191 ms  deceze2
191 ms  192 ms  192 ms  deceze3

(Didn't include @buhtz's because it's not quite comparable.)

Benchmark code (Try it online! - Python 2 version):

def original():
    for i in list1:
        if i < switchValue:
            print(i)
        else:
            for j in list2:
                print(j)
            break

def deceze1():
    for i in list1:
        if i >= switchValue:
            break
        print(i)
    for i in list2:
        print(i)

def deceze2():
    for i in takewhile(lambda i: i < switchValue, list1):
        print(i)
    for i in list2:
        print(i)

def deceze3():
    for i in chain(takewhile(lambda i: i < switchValue, list1), list2):
        print(i)

def Kelly1():
    switchIndex = bisect_left(list1, switchValue)
    print(*list1[:switchIndex], sep='\n')
    print(*list2, sep='\n')

def Kelly2():
    switchIndex = bisect_left(list1, switchValue)
    print('\n'.join(map(str, list1[:switchIndex])))
    print('\n'.join(map(str, list2)))

funcs = original, deceze1, deceze2, deceze3, Kelly1, Kelly2

import os, sys
from timeit import default_timer as timer
from bisect import bisect_left
from itertools import takewhile, chain

list1 = [1, 3, 5, 8]
list2 = [7, 10, 12]
switchValue = 4

for func in funcs:
    print(func.__name__   ':')
    func()

n = 100_000
list1 = [x for x in list1 for _ in range(n)]
list2 = [x for x in list2 for _ in range(n)]

print('benchmark:')
tss = [[] for _ in funcs]
for _ in range(10):
    for func, ts in zip(funcs, tss):
        with open(os.devnull, 'w') as sys.stdout:
            t0 = timer()
            func()
            t1 = timer()
        sys.stdout = sys.__stdout__
        ts.append(t1 - t0)
        ts.sort()
for func, ts in sorted(zip(funcs, tss), key=lambda x: x[1]):
    print(*('%d ms ' % (t * 1e3) for t in sorted(ts)[:3]), func.__name__)
  • Related