Frank Brewer
Frank Brewer

Reputation: 13

Python - checking whether indexed element of a list exist - Best Practice?

I have something like this:

for i in range(0,100,5):

    text = ("Price\t",
            foo[i+0].bar, "\t",
            foo[i+1].bar, "\t",
            foo[i+2].bar, "\t",
            foo[i+3].bar, "\t",
            foo[i+4].bar, "\t",
            "Value", "\n")

    file.writelines(text)

My problem is: let's assume that i=0. For that case, it is certain that I will have foo[0]. But for indices 1,2,3,4 foo can be empty. For example, if foo is empty for indices greater than 2, I want my text to be like:

text = ("Price\t",
        foo[0].bar, "\t",
        foo[1].bar, "\t",
        foo[2].bar, "\n")

I thought of using exceptions, but I guess, I will not be able to construct text if I do not have all the indexed elements. (Iteration will stop?) So, what is the best practice to do that? I could not think of a short way to do it. Basically what I need is:

text= ("Price\t",
        ifexist(foo[0].bar, "\t"),
        ifexist(foo[1].bar, "\t"),
        ifexist(foo[2].bar, "\t"),
        ifexist(foo[3].bar, "\t"),
        ifexist(foo[4].bar, "\t"),
        "Value", "\n")

ps: Please do not forget I assumed i=0 for the sake of simplicity. But in fact, generally I am going to have more than hundred values.

edit: By saying "can be empty", I meant to say that the index might be beyond the size of the list.

edit2: Abot foo:

# start class
class Test:
    def __init__(self, bar, bar2, bar3):
        self.bar= a
        self.bar2= b
        self.bar3= c

# end class    


for i in particular_list:
    # read and parse the input file with indeces obtained from particular_list
    # do calculations for temp_bar,temp_bar2,temp_bar3
    foo.append(Test(temp_bar, temp_bar2, temp_bar3))

Upvotes: 1

Views: 126

Answers (4)

Roland Smith
Roland Smith

Reputation: 43533

It depends on what foo is. If it is a list or tuple, there will be no empty indices. If it is a dict, referencing an unknown key will result in a KeyError exception.

Assuming foo is a list of tuples, you can easily print it;

In [3]: t = range(30)

In [4]: p = [tuple(t[i:i+3]) for i in range(0, len(t), 4)]

In [5]: p
Out[5]: [(0, 1, 2), (4, 5, 6), (8, 9, 10), (12, 13, 14), (16, 17, 18), (20, 21, 22), (24, 25, 26), (28, 29)]

You can iterate over that list of tuples and print them;

In [6]: for k in p:
    print '\t'.join(['Price'] + [str(i) for i in k] + ['Value'])
   ...:     
Price   0   1   2   Value
Price   4   5   6   Value
Price   8   9   10  Value
Price   12  13  14  Value
Price   16  17  18  Value
Price   20  21  22  Value
Price   24  25  26  Value
Price   28  29  Value

Upvotes: 0

Abhijit
Abhijit

Reputation: 63757

My suggestion would be, do not rely on indices but rather structure your code around your data. If you are not indexing, then you would never have issues in IndexError

Consider A List L of size N to be divided evenly of size n. This problem has various accepted solutions and approaches

The one I generally preach (though you are free to accept any of the alternate approaches) is

izip_longest(*[iter(L)]*n)

So Given a List

L = [a1, a2, a3 ... aN]

This will generate ⌊N/n⌋ equally sized chunks. The last shorter lists of size Mod(N, n) would be appended by a filler, default in this case is None

L = [[a1, a2, a3 ... an],[a1, a2, a3 ... an],[a1, a2, a3 ... an],.(N Items)..[a1, a2, .. aN%n],None,None.. (n terms)]

Now just iterate through this list of lists ignorining any Nones in the sublists

Demo

from itertools import izip_longest
class Foo(object):
    def __init__(self, n):
        self.bar = n
foo = [Foo(i) for i in range(12)]
for rows in izip_longest(*[iter(foo)]*5):
    print "Price\t{}".format('\t'.join(str(row.bar)
                       for row in rows
                       if row is not None))

Output

Price   0   1   2   3   4
Price   5   6   7   8   9
Price   10  11

Upvotes: 1

jonrsharpe
jonrsharpe

Reputation: 122105

To expand on my comment, you could implement something like:

def if_exist(seq, index, attr, default):
    """Return seq[index].attr or default."""
    try:
        return getattr(seq[index], attr)
    except IndexError:
        return default

Which you could use like:

if_exist(foo, 0, "bar", "\t")

A demo:

>>> if_exist([1, 2, 3], 4, "bar", "\n")
'\n'

However, I suspect this to be an XY problem - if you provide more background information, it is likely that we can help you come up with a different approach that removes this issue entirely.

Upvotes: 0

Martijn Pieters
Martijn Pieters

Reputation: 1123640

You would take a different approach; you'd slice the original list, and use the csv module to handle tab-delimiting and newlines:

import csv

with open('outputfile', 'w', newline='') as file:
    writer = csv.writer(file, delimiter='\t')
    for i in range(0, 100, 5):
        row = foo[i:i + 5]
        writer.writerow(['Price'] + row + ['Value']) 

Slicing on a list always returns a new list object, but if you use slice indices outside the valid range the result is a shorter or empty list:

>>> foo = ['spam', 'ham', 'eggs']
>>> foo[0:5]
['spam', 'ham', 'eggs']
>>> foo[5:10]
[]

The csv.writer() object, meanwhile, takes care of writing the list as a tab-delimited string, plus a newline, to your file.

Instead of using the csv module, you would still use slicing, but make use of other techniques that take arbitrary lists of elements. str.join() for example:

'\t'.join(['Price'] + foo[i:i + 5] + ['Value']) + '\n'

would produce one string of tab-delimited values, with a newline appended. This does require that all values in the list passed to str.join() are already strings.

Upvotes: 2

Related Questions