4
\$\begingroup\$

Given a text I have to find the preceding words to all numbers up to a stop word belonging to a check_words list (kind of stopwords).

My code:

check_words = ['the', 'a', 'with','to']
mystring = 'the code to find the beautiful words 78 that i have to nicely check 45 with the snippet'
list_of_words = mystring.split()

In that particular text I would check before '78' and '45' and will go backwards up to the point where I find any of the words in check_words (but not more than 8 words).

The code for doing that might be:

preceding_chunks = []
for i,word in enumerate(list_of_words):
    if any(char.isdigit() for char in word):
       
        # 8 precedent words (taking into account that I can not slice with 8 back at the beginning)
        preceding_words = list_of_words[max(0,i-8):i]
        preceding_words[::-1]

        # I check from the end of the list towards the start
        for j,sub_word in enumerate(preceding_words[::-1]):
            if  sub_word in check_words:
                # printing out j for checking
                myposition = j
                print(j)
                real_preceding_chunk = preceding_words[len(preceding_words)-j:]
                print(real_preceding_chunk)
                preceding_chunks.append(real_preceding_chunk)
                break

This code works. basically I check every word tha But I have the impression (perhaps I am wrong) that it can be achieved with a couple of one liners and hence without loops. Any idea?


NOTE: This question is about improvement the readability of the code, trying to get rid of the loops to make the code faster, and trying to make the code nicer, which is part of the Zen of Python.


NOTE 2: Some previous checks that I did:

\$\endgroup\$
1

1 Answer 1

0
\$\begingroup\$

I came up with this:

import itertools
import re

chunks = (grouped_chunk.split() for grouped_chunk in re.split("\\s+\\d+\\s+", mystring))
preceding_chunks = []

for reversed_chunk in map(reversed, chunks):
    preceding_chunk = list(itertools.takewhile(lambda word: word not in check_words, reversed_chunk))[::-1]
    preceding_chunks.append(preceding_chunk)

We apply itertools.takewhile to the reversed_chunk which gives us the preceding chunk in reversed order. We then obtain the correctly ordered preceding_chunk by reversing at the end with [::-1].

The regex splits mystring based on a number (the escaped \d+). The surrounding escaped \s+s represent any padding around the number. This causes this code to have different behavior than yours if digits and letters are mixed in the same words (for example, a1).

For your original code, I'd make a couple suggestions:

  1. Follow PEP 8. For example, add spacing after the comma in i,word.
  2. Remove the redundant expression preceding_words[::-1]. While this does evaluate to the reversed preceding_words, because it is not in-place, the evaluation has no side-effects. Plus, you already perform this reversal in enumerate(preceding_words[::-1]).
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.