user2242044
user2242044

Reputation: 9213

Countif list of list - list comprehension

I would like to iterate over the values in the list Dates and first append the count of items in Date_Values[0] less than the value in Date then append the count of items in Date_Values[1] less than the value in Date

import datetime

Dates_Values = [['10-12-2014','11-1-2014'],['10-18-2014','10-26-2014']]
Dates = [['10-14-2014'],['10-19-2015']]

for key, item in enumerate(Dates):
    Dates[key].append(sum(1 for item in Dates_Values if datetime.datetime.strptime(str(item[0]), '%m-%d-%Y') > (datetime.datetime.strptime(str(item[0]), '%m-%d-%Y'))))
    Dates[key].append(sum(1 for item in Dates_Values if datetime.datetime.strptime(str(item[1]), '%m-%d-%Y') > (datetime.datetime.strptime(str(item[1]), '%m-%d-%Y'))))

print Dates

Output:

[['10-14-2014', 0, 0], ['10-19-2015', 0, 0]]

Desired Results:

[['10-14-2014',1,0],['10-19-2015',2,2]]

Upvotes: 0

Views: 574

Answers (2)

lvc
lvc

Reputation: 35059

It helps to simplify your code a bit. The single best thing you can do here is to notice that you use this invocation:

datetime.datetime.strptime(arg, '%m-%d-%Y')

four times, and all that changes is the value of arg. So put it in a function:

def parse_date(datestamp):
   return datetime.datetime.strptime(str(datestamp), '%m-%d-%Y')

You can also sum bools directly instead of using the awkward sum(1 for item in ...). Putting them together you get this:

for key, item in enumerate(Dates):
    Dates[key].append(sum(parse_date(item[0]) > parse_date(item[0]) for item in Dates_Values))
    Dates[key].append(sum(parse_date(item[1]) > parse_date(item[1]) for item in Dates_Values))

This makes your bug much easier to spot: the things you're comparing are the same, and hence will always be equal. You seem to be confusing yourself by reusing the name item in both the outer loop and the generator expressions - you are expecting the left-hand side of each comparison to use the outer item, and the right to use the inner one. You can get away with this by parsing the outer date out first (which is a good idea anyway), but it is better to avoid shadowing the variable like this because you won't always be able to do that. Also, since Dates[key] is just item in the outer loop, you actually don't need to use enumerate at all:

for item in Dates:
    date = parse_date(item[0])

    item.append(sum(date > parse_date(i[0]) for i in Dates_Values))
    item.append(sum(date > parse_date(i[1]) for i in Dates_Values))

Also, Python convention is that variable and function names should be all lower-case - ie, dates rather than Dates etc; initial capitals are reserved for class names. This doesn't affect how your code works in the slightest, but it can make it easier for you and others to read.

Upvotes: 1

Barry
Barry

Reputation: 302842

Basically, we want to transform every element of Dates. So that tells us we want to start with:

Dates = [f(d) for d in Dates]

From there we can either just build up what f is:

def count(d, idx):
    return sum(1 for item in Dates_Values if ...) # use item[idx] where
                                                  # you had item[0], item[1]

Dates = [[d, count(d, 0), count(d, 1)] for d in Dates]

We could of course write the definition of count inline, kind of the way you had it, but that to me is much less readable:

Dates = [[d] + [sum(1 for item in Dates_Values if...) for idx in (0,1)]
         for d in Dates]

Upvotes: 0

Related Questions