[SOLVED] Why does comparing two Counter.items() with <= give unexpected results?

Issue

I am trying to make a game where you can form all the different words from one other word by scrambling some letters from the main word. I have a list of subwords, but if the main word has a double letter (Like ‘r’ in this case) it won’t add strings with a single r to the victory list. How can I fix this?

from collections import Counter
victory = []

if __name__ == "__main__":
    main_word = "burner"
    subwrds = ['run', 'rue', 'brr', 'bun', 'bur', 'err', 'nub', 'rub', 'urn', 'burr',
                'rube', 'burn', 'rune', 'rerun', 'burner', 'bee', 'ebb']

    mw_counter = Counter(main_word)
    mw_key = list(mw_counter.keys())
    print(mw_key)
    mw_value = list(mw_counter.values())
    for wrd in range(len(subwrds)):
        subwrds_count = Counter(subwrds[wrd])
        subwrds_key = list(subwrds_count.keys())
        print(list(subwrds[wrd]))

        if subwrds_count.items() <= mw_counter.items():
            victory.append(subwrds[wrd])
    print(victory)

Solution

The problem is this line:

if subwrds_count.items() <= mw_counter.items():

The .items() method of a dictionary (or Counter) returns a view object over (key, value) pairs, which behaves like a set.
Counter(X).items() <= Counter(Y).items() will be true if and only if the frequency of each member of X is the same as its frequency in Y.

For example:

Counter('b').items() <= Counter('bb').items()  # False

is False, because {('b', 1)} is not a subset of {('b', 2)}.

If you have Python 3.10 or higher, Counter now supports rich comparison, so the ‘word scramble’ relationship (i.e. multiset containment) can be checked with:

if subwrds_count <= mw_counter:

where you compare two Counter objects directly. Otherwise, you can just use

if all(subwrds_count[x] <= mw_counter[x] for x in subwrds_count):

Answered By – kcsquared

Answer Checked By – Willingham (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published.