1

I'm new to python (although it's more of a logical question rather than syntax question I belive), and I wonder what's the proper way to access two folowing objects in a loop.

I can't really provide a specific example without getting too cumbersome with my explanation but let's just say that I usually try to tackle this with either [index + 1] or [index - 1] and both are problematic when it comes to either the last (IndexError) or first (addresses the last position right at the beginning) iterations respectively.

Is there a well known way to address this? I haven't really seen any questions regarding this floating around so it made me think it's basic logic I'm missing here.

For example this peice of code that wouldn't have worked had I not wrapped everything with try/except, and also the second inner loop works only since it checks for identical characters, otherwise it could have been a mess.

(explanation for clarity - it recieves a string (my_string) and a number (k) and checks whether a sequence of identical characters the length of k exists in my_string)

# ex2 5
my_string = 'abaadddefggg'
sub_my_string = ''
k = 9
count3 = 0

try:
    for index in range(len(my_string)):
        i = 0
        while i < k:
            sub_my_string += my_string[index + i]
            i += 1

        for index2 in range(len(sub_my_string)):
            if sub_my_string[index2] == sub_my_string[index2 - 1]:
                count3 += 1

        if count3 == k:
            break
        else:
            sub_my_string = ""
            count3 = 0

    print(f"For length {k}, found the substring {sub_my_string}!")

except IndexError:
    print(f"Didn't find a substring of length {k}")

Thanks a lot

quamrana
  • 37,849
  • 12
  • 53
  • 71
wambam97
  • 33
  • 4

2 Answers2

2

First off, by definition you need to give special attention to the first or last element, because they really don't have a pair.
Second-off, I personally tend to use list-comprehensions of the following type for these cases -

[something_about_the_two_consecutive_elements(x, y) for x, y in zip(my_list, my_list[1:])]

And last but not least, the whole code snippet seems like major overkill. How about a simple one-liner -

my_string = 'abaadddefggg'
k = 3

existing_substrings = ([x * k for x in set(my_string) if x * k in my_string])

print(f'For length {k}, found substrings {existing_substrings}')

(To be adapted by one's needs of course)


Explanation:
For each of the unique characters in the string, we can check if a string of that character repeated k times appears in my_string.
set(my_string) gives a set of the unique characters over which we iterate (that's the for x in set(my_string) in the list comprehension).
Taking a character x and multiplying by k gives a string xx...x of length k.
So x * k in my_string tests whether my_string includes the substring xx...x.
Summing up the list-comprehension, we return only characters for which x * k in my_string is True.

ShlomiF
  • 2,686
  • 1
  • 14
  • 19
  • I agree. But there was something to say about the rest of the question and the specific use-case, so I thought it might be worth answering... – ShlomiF Jul 11 '21 at 19:32
  • btw, can't You just use `x * k` instead of `''.join([x] * k)`? also is it simple? I guess it is or at least it is not that complicated however it is not too readable (especially for beginners) – Matiiss Jul 11 '21 at 19:36
  • Thanks for the help. Yeah I figured it might be the case (giving the first and last elements special treatment). I'm not yet at a level this one liner is any help though, I want to be able to logically understand what I'm doing (I'm really only starting out) instead of focusing on efficiancy solely (even though it's all intertwined in the end). – wambam97 Jul 11 '21 at 20:34
  • 1
    I'll add an explanation – ShlomiF Jul 11 '21 at 21:18
  • Thanks! Makes a lot of sense to tackle it this way. – wambam97 Jul 13 '21 at 09:54
1

If I am understanding what you are trying to achieve, I would approach this differently using string slices and a set.

my_string = "abaadddefggg"
sub_my_string = ""
k = 3
count3 = 0

found = False
for index, _ in enumerate(my_string):
    if index + k > len(my_string):
        continue
    sub_my_string = my_string[index : index + k]

    if len(set(sub_my_string)) == 1:
        found = True
        break

if found:
    print(f"For length {k}, found the substring {sub_my_string}!")
else:
    print(f"Didn't find a substring of length {k}")

Here we use:

  • enumerate as this usually signals that we are looking at the indices of an iterable.
  • Check whether the slice will be take us over the string length as there's no point in checking these.
  • Use the string slice to subset the string
  • Use the set to see if all the characters are the same.
Alex
  • 6,610
  • 3
  • 20
  • 38
  • 2
    It's neither exaggerated nor unnecessarily complicated. It's a valid approach to solving the problem, just because you would do it differently doesn't make it wrong. – Alex Jul 11 '21 at 19:36
  • Of course it's valid. I'm not saying it's wrong. Just much more complex than necessary. – ShlomiF Jul 11 '21 at 19:39
  • 1
    Again I disagree, OP has said they are new to using Python. I am demonstrating techniques that solve the issue they have posted in a way that (IMO) complements what they were attempting. – Alex Jul 11 '21 at 19:44
  • 1
    Thanks a lot. There was a certain syntax gap so I coudn't get it on the first read but it makes sence now after reading into it. Guess I should keep working on getting to know the syntax better while I'm at it. Just a question though, what's the purpose of _ beside index in the loop? – wambam97 Jul 11 '21 at 20:29
  • 1
    That's a good question. enumerate yields tuples in the form `(index, value)`, as we are only interested in the index the `_` is saying "we get this extra return, but aren't using it". Does that make sense? – Alex Jul 11 '21 at 20:32
  • 1
    Makes perfect sense. Thanks so much. – wambam97 Jul 11 '21 at 20:36