group almost duplicate strings I am using the following script to fuzzy search almost similar book names for duplicate:
import re
from nltk.util import ngrams

OriginalBooksList = list()
booksAfterRemovingStopWords = list()
booksWithNGrams = list()
duplicatesSorted = list()

stopWords = ['I', 'a', 'about', 'an', 'are', 'as', 'at', 'be', 'by', 'com', 'for', 'from', 'how', 'in', 'is', 'it', 'of', 'on', 'or', 'that', 'the', 'this', 'to', 'was', 'the',
             'and', 'A', 'About', 'An', 'Are', 'As', 'At', 'Be', 'By', 'Com', 'For', 'From', 'How', 'In', 'Is', 'It', 'Of', 'On', 'Or', 'That', 'The', 'This', 'To', 'Was', 'The', 'And']

with open('UnifiedBookList.txt') as fin:
    for line_no, line in enumerate(fin):
        OriginalBooksList.append(line)
        line = re.sub(r'[^\w\s]', ' ', line)  # replace punctuation with space
        line = re.sub(' +', ' ', line)  # replace multiple space with one
        line = line.lower()  # to lower case
        if line.strip() and len(line.split()) &gt; 2:  # line can not be empty and line must have more than 2 words
            booksAfterRemovingStopWords.append(' '.join([i for i in line.split(
            ) if i not in stopWords]))  # Remove Stop Words And Make Sentence


for line_no, line in enumerate(booksAfterRemovingStopWords):
    tokens = line.split(&quot; &quot;)
    output = list(ngrams(tokens, 3))
    temp = list()

    temp.append(OriginalBooksList[line_no])  # Adding original line
    for x in output:  # Adding n-grams
        temp.append(' '.join(x))
    booksWithNGrams.append(temp)

while booksWithNGrams:
    first_element = booksWithNGrams.pop(0)
    x = 0
    for mylist in booksWithNGrams:
        if set(first_element) &amp; set(mylist):
            if x == 0:
                duplicatesSorted.append(first_element[0])
                x = 1
            duplicatesSorted.append(mylist[0])
            booksWithNGrams.remove(mylist)
    x = 0

with open('DuplicatesSorted.txt', 'w') as f:
    for item in duplicatesSorted:
        f.write(&quot;%s\n&quot; % item)

The input is:
A Course of Pure Mathematics by G. H. Hardy
Agile Software Development, Principles, Patterns, and Practices by Robert C. Martin
Advanced Programming in the UNIX Environment, 3rd Edition
Advanced Selling Strategies: Brian Tracy
Advanced Programming in the UNIX(R) Environment
Alex's Adventures in Numberland: Dispatches from the Wonderful World of Mathematics by Alex Bellos, Andy Riley
Advertising Secrets of the Written Word: The Ultimate Resource on How to Write Powerful Advertising
Agile Software Development, Principles, Patterns, and Practices
A Course of Pure Mathematics (Cambridge Mathematical Library) 10th Edition by G. H. Hardy 
Alex’s Adventures in Numberland
Advertising Secrets of the Written Word
Alex's Adventures in Numberland Paperback by Alex Bellos

The output is:
A Course of Pure Mathematics by G. H. Hardy

A Course of Pure Mathematics (Cambridge Mathematical Library) 10th Edition by G. H. Hardy 

Agile Software Development, Principles, Patterns, and Practices by Robert C. Martin

Agile Software Development, Principles, Patterns, and Practices

Advanced Programming in the UNIX Environment, 3rd Edition

Advanced Programming in the UNIX(R) Environment

Alex's Adventures in Numberland: Dispatches from the Wonderful World of Mathematics by Alex Bellos, Andy Riley

Alex’s Adventures in Numberland

Alex's Adventures in Numberland Paperback by Alex Bellos

Advertising Secrets of the Written Word: The Ultimate Resource on How to Write Powerful Advertising

Advertising Secrets of the Written Word

By looking at the script, it seems to me that I have over complicated things. Please give some suggestion on how I can make this code better.
Ok, I tried to rearrange it a bit:

Corrected stop words (Should only contain lowercase words)
Used Jaccard method to calculate distances
Rearranged code structure
Rewrote it in Python3 with type annotations

You should now add an argument parser and that's basically it.
As far as I understood the task, the final goal was to remove same books.
Now you can play with a threshold argument to find out what strings should be treated the same.
import re
from typing import List, Callable, Set

from nltk.metrics.distance import jaccard_distance
from nltk.util import ngrams


def canonize(data: str) -&gt; str:
    data = re.sub(r'[^\w\s]', ' ', data)  # replace punctuation with space
    data = re.sub(' +', ' ', data)  # replace multiple space with one
    return data.lower().strip()


def jaccard(book_a: str, book_b: str, n: int = 3) -&gt; float:
    return 1 - jaccard_distance(set(ngrams(book_a, n)), set(ngrams(book_b, n)))


def filter_books(books: List[str],
                 book_filter_fun: Callable,
                 cmp_filter_func: Callable,
                 threshold: float = 0.3) -&gt; Set[int]:
    excluded_indices = set()
    for one_book_offset, one_book in enumerate(books):
        if book_filter_fun(one_book):
            excluded_indices.add(one_book_offset)
        for another_book_offset, another_book in enumerate(books[one_book_offset + 1:], start=one_book_offset + 1):
            if {one_book_offset, another_book_offset} &amp; excluded_indices:
                continue
            if cmp_filter_func(one_book, another_book) &gt; threshold:
                excluded_indices.add(one_book_offset)
    return excluded_indices


if __name__ == '__main__':
    stopWords = {'i', 'a', 'about', 'an', 'are', 'as', 'at', 'be', 'by', 'com', 'for', 'from', 'how', 'in', 'is', 'it',
                 'of', 'on', 'or', 'that', 'the', 'this', 'to', 'was', 'the'}

    with open('UnifiedBookList.txt') as fin:
        original_books = fin.readlines()

    canonized_books = list(map(canonize, original_books))

    excluded_indices = filter_books(
        canonized_books,
        lambda book: len(book.split()) &lt; 2,  # book name should contain not less than 2 words
        jaccard,
    )

    with open('DuplicatesSorted.txt', 'w') as fout:
        for i, book in enumerate(original_books):
            if i in excluded_indices:
                continue
            fout.write(book)