1

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))
  • 1
    The minimal fix is to `try` and then catch `except IndexError`. But you are probably better off changing your data structure - why not e.g. a list of tuples instead of a long, flat list? – jonrsharpe Jul 31 '14 at 16:35
  • What do you mean by `empty`? Do you mean the index is beyond the size of the list? Or that the value of `foo[4]` is `None`? Or that the value of `foo[4]` is `''`? – Robᵩ Jul 31 '14 at 16:49
  • @jonrsharpe using a class structure to store the elements seemed like a neat thing to do. but tbh, it's been an intuitive desicion. I tried to use try&except IndexError, but in that case, if one of the elements is missing, I lose the `text`. Could you please show me how to use it? – Frank Brewer Jul 31 '14 at 16:56
  • @Robᵩ I was trying to say if the index is beyond the size of the list. Thank you, I will edit the question. – Frank Brewer Jul 31 '14 at 16:57
  • @FrankBrewer Your `Test` class might as well be a tuple since it contains no methods. That would make `foo` a list of tuples that could be easily processed by iterating over the list and `join`-ing the tuples. – Roland Smith Jul 31 '14 at 17:46

4 Answers4

2

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.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
1

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
Community
  • 1
  • 1
Abhijit
  • 62,056
  • 18
  • 131
  • 204
0

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
Roland Smith
  • 42,427
  • 3
  • 64
  • 94
0

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.

Community
  • 1
  • 1
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437