11

I wrote this program in python to see if a list is ascending and its not working, can someone help me?

list1 = [1, 2, 3, 4]
print (list1)
length = len(list1)
run_started = False
for x in range(length - 1):
    t = list1[x + 1] - list1[x]
    if t > 0 :
        if run_started:
            run_length = x
        else:
            run_started = True
            run_length = x
    else:
        if run_started:
            print (True)
            print ("Run Length: {0}".format(run_length))
            break
if not run_started:
    print (False)
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
user2980776
  • 123
  • 1
  • 1
  • 6
  • 3
    What exactly is wrong? `Its not working` isn't enough. – That1Guy Nov 11 '13 at 21:19
  • Can you tell us what you are receiving as output? –  Nov 11 '13 at 21:22
  • is your list always sorted? and you just need to check ascending or descending? – Joran Beasley Nov 11 '13 at 21:23
  • as far as I can tell this code does work though.. it just only produces output if the list is not in ascending order. – OGHaza Nov 11 '13 at 21:29
  • its not tellimg me where it starts ascending and how long it is ascending for – user2980776 Nov 11 '13 at 21:53
  • Just an FYI: after the OP commented, it's pretty clear this is *not* a duplicate. The question here is about how to calculate the length of the run of ascending values from the start. It is *not* about how to merely check whether the array is in ascending order. I think it should be re-opened, especially since none of the answers at the supposed "duplicate" question addresses this. – ely Nov 12 '13 at 19:36

5 Answers5

26

I'd say the easiest (although not the most efficient) way would be:

list1 = [3, 1, 2, 4]

if sorted(list1) == list1:
    print "list1 is sorted"
jbat100
  • 16,757
  • 4
  • 45
  • 70
  • 3
    or just `sorted(the_list) == the_list` – Joran Beasley Nov 11 '13 at 21:25
  • good point, this is a better answer... – jbat100 Nov 11 '13 at 21:27
  • @EMS the OP's question is "I wrote this program in python to see if a list is ascending and its not working", so my answer is a suggestion for how to do it. He didn't state that he wanted to know the first unsorted element until the 7th comment. – jbat100 Nov 11 '13 at 22:17
10

Well, here's my go at it. Some have suggested sorting the list, which would be O(nlogn). I propose a simpler O(n) solution

def isAscending(list):
    previous = list[0]
    for number in list:
        if number < previous:
            return False
        previous = number
    return True
slider
  • 12,810
  • 1
  • 26
  • 42
5

How about a one-liner to tell if every number in the list x is strictly increasing?

[(x[k+1]-x[k])>0 for k in range(len(x)-1)].count(True) == len(x)-1
Greg von Winckel
  • 2,261
  • 2
  • 16
  • 14
1

I think the OP is asking what is wrong with their own code. If that's the case, here goes:

It looks like you never reach the end condition that you want. I mean, you get your run_started variable to get to True. But then what? Nothing else happens in your code since you never trigger the else statement that is contrary to your if t > 0 statement. I think you should re-think the logic in your code and what you want it to do exactly

EDIT: your code commented, I've put numbers in {{}} to show what happens in what order

list1 = [1, 2, 3, 4]
print (list1)
length = len(list1)
run_started = False
for x in range(length - 1): # Loop begins {{1}}
    t = list1[x + 1] - list1[x]
    if t > 0 : 
        if run_started:
            run_length = x #You keep setting a new run_length {{3}}
        else:
            run_started = True #You've set run_started  {{2}}
            run_length = x
    else:
        if run_started:
            print (True)
            print ("Run Length: {0}".format(run_length))
            break
if not run_started: #Then this is called after the loop {{4}} but it doesn't return true
    print (False)

As you can see, you exit the for loop without ever calling else on line 13. Furthermore then final if not run_started is also never called. Your code works the way it is designed to work, though maybe not the way you want it to work. HTH

  • im trying to make it so it tells when it starts acending and for how long – user2980776 Nov 11 '13 at 21:53
  • Well, since your test data is a list that is always ascending, your code will never output anything of use. What you can do (if you want to keep the same test data), is provide an else statement to go along with your "if not run_started". In that else statement, you can print out run_length. That way, if the run "has started", you get output. –  Nov 11 '13 at 22:02
  • where would i put that – user2980776 Nov 11 '13 at 22:04
  • You said you want it to tell you when it starts ascending and how long it's ascending right? Well, here's an option. You put print("Started ascending") after run_started=True. Then, you need to print out your run_length in every iteration of your for loop, so you can keep everything the same but add print ("Run Length: {0}".format(run_length)) in your if t > 0: if run_started: block. That way, no matter what happens, every iteration where the run has started, you print out run_length. –  Nov 12 '13 at 00:15
0

A way that is more efficient than fully constructing the sorted list (and should work for arbitrary iterable types, containing any data types that allow for greater-than comparisons):

def len_ascending_run_from_start(seq):
    vals = enumerate(seq)
    try:
        c,x = 1, vals.next()[1]
    except StopIteration:
        raise ValueError("Sequence is empty!")
    for i, cur_x in vals:
        if cur_x < x:
            break
        c,x = c+1, cur_x
    return c

E.g.:

In [44]: len_ascending_run_from_start([datetime.date(2012,1,4), datetime.date(2012,1,3), datetime.date(2012,1,5)])
Out[44]: 1

In [45]: len_ascending_run_from_start([datetime.date(2012,1,4), datetime.date(2012,1,6), datetime.date(2012,1,5)])
Out[45]: 2

In [46]: len_ascending_run_from_start([datetime.date(2012,1,4), datetime.date(2012,1,6), datetime.date(2012,1,7)])
Out[46]: 3

In [47]: len_ascending_run_from_start((1,2,3,4))
Out[47]: 4

In [48]: len_ascending_run_from_start((1,3,2,4))
Out[48]: 2

In [49]: len_ascending_run_from_start(set([1,3,2,4]))
Out[49]: 4

It could also be fun / useful to make a class such that len automatically reports this statistic about the underlying sequence (and keeps a simplistic cache of the result, to avoid calculating it repeatedly).

class AscendingRunFromStart(object):
    def __init__(self, seq):
        self.seq = seq

    def __repr__(self):
        return self.seq.__repr__()
    __str__ = __repr__

    def __len__(self):
        if hasattr(self, "_len"):
            return self._len
        vals = enumerate(self.seq)
        c,x = 1, vals.next()[1]
        for i, cur_x in vals:
            if cur_x < x:
                break
            c,x = c+1, cur_x
        self._len = c
        return self._len

E.g.:

In [76]: x = AscendingRunFromStart([1,3,2,4])

In [77]: x
Out[77]: [1, 3, 2, 4]

In [78]: len(x)
Out[78]: 2

A good extension would be to use a Descriptor pattern to make the seq attribute of the class read-only, or to invoke an updated len calculation whenever set is called. (Left as exercise for the reader...)

ely
  • 74,674
  • 34
  • 147
  • 228
  • Although this is clearly faster (and doesn't require a copy) than the answer I posted, it has the drawback of preventing a custom comparison function to be given (one other than <). And of being ten times longer. And really the class with overridden __repr__ and __len__ is fine for a bit of fun, but it is also a good way for people who maintain your code to commit suicide. – jbat100 Nov 11 '13 at 22:36
  • all i'm saying is i would use mine unless performance was an issue. It often isn't, and i don't think my answer is either terrible nor useless. Goodbye. – jbat100 Nov 12 '13 at 19:19
  • it's funny. It seems the community disagrees. – jbat100 Nov 12 '13 at 22:28
  • it does answer the question, it does not address the (extra) requirement added (after my answer) in the 5th comment. – jbat100 Nov 13 '13 at 15:09