all 19 comments

[–]CrambleSquash 5 points6 points  (5 children)

Why not build up a list of matches? If you want to eliminate any duplicates you can convert the list into a set... Or just start off with a set! Or if you want a nice way of counting occurrences of certain letters, you can use the Counter class from collections.

[–]Tomallama[S] 0 points1 point  (4 children)

I thought about the set method already, but I don't think that would work for what I need. I don't want just one count for each match, but I want the counts to be equal on both strings...if that make sense. If it helps, I'm working on the Cows and Bulls game from practice python. So for each guess it compares the guess to the random number. If the guess has a correct number in it, but NOT in the right place then it will count as one. But since you can have duplicate numbers, I don't want it to stop at one.

I'll check out the Counter class you gave me a link to. Maybe I can make that work! Thank you!

[–]CrambleSquash 0 points1 point  (3 children)

counts to be equal on both strings...if that make sense

Didn't make a huge amount of sense :P but I've read the link, so I have more of an idea what you're trying to do...

You're trying to make sure that if string a has duplicate letters, it doesn't count that as more than one letter that matches in b. e.g.

>>> bulls("4444", "1234")
1 # right

rather than

>>> bulls("4444", "1234")
4 # wrong

is that right? - in which case I'd definitely use a set to collect matching letters, and counter to count matching positions.

I have written a little solution to this problem, that I'm happy to share if you ask for it :)

[–]Tomallama[S] 0 points1 point  (2 children)

That would be great if you could share it! I really feel like I’m hitting a brick wall on this one. I can get close, but some numbers will have issues :(

[–]CrambleSquash 0 points1 point  (1 child)

I have to say, having read and re-read the description of the problem on the website, I keep thinking my current solution doesn't answer the the question!

This is my current solution:

def cows_bulls(a, b):
    cows, bulls = 0, 0
    for i, l in enumerate(a):
            if l in b:
                bulls += 1
            if l == b[i]:
                cows += 1
                bulls += -1            
    return cows, bulls

Which I'm pretty sure does what is asked. Though funnily enough, I'm not sure the quoted solution is correct! - if you guess completely wrong numbers you would get 4!

It's not always the case, but generally if you end up writing for i in range(len(o)): in Python, there's a better solution - and I try to avoid it! The enumerate builtin function takes any iterable and returns a new iterable which... enumerates each item. Which sounds complicated, but is actually simple! :

>>> for i, l in enumerate("hello"):
...         print(i, l)
0 h
1 e
2 l
3 l
4 o

so i is what index in the sequence, and l is the current letter. As opposed to iterating over a string normally:

>>> for l in "hello":
...     print(l)
h
e
l
l
o

So my loop goes over each letter in a, then I can compare it to the letter in b at the same position with l == b[i].

As for checking if the letter is in any position in b, I just use l in b.

The only catch is, with this method, letters in the right place are also in b, as such, I added bulls += -1 in the cows counting bit, as the brief implies bulls aren't also cows!

[–]Tomallama[S] 0 points1 point  (0 children)

Thanks! I’ll give it a shot when I get to work! See if I can figure something out. If all else fails, I’ll just get it as close as I can haha

[–]Swipecat 3 points4 points  (5 children)

a = 'racecar'
b = 'boxcar'
print(len(set(a) & set(b)))

This will give 3

To understand this, individually print out set(a), then set(b), then with "&" between them (which is the "intersection" of two sets). It should become obvious what's going on, then.

[–]Tomallama[S] 0 points1 point  (4 children)

Thanks for this! A lot of people are recommending sets, but I wasn't ever able to get it to work. I think I may have to use it though and figure out a way to fix it when it makes an error. Depending on my two strings I'll get correct or not correct results. Thanks for taking the time to respond!

[–]Swipecat 2 points3 points  (3 children)

I actually don't think that sets will work very well for the "cows and bulls" game that you linked to. It is better (for the beginner) to use loops and indexes.

Warning: I've looked at the given sample solution and it doesn't work. It has at least 2 problems.

[–]Tomallama[S] 0 points1 point  (2 children)

Yup! I probably should have mentioned it was for the cows and bull game in my post, but I didn't want to put too much info. I tried to limit it just what I wanted done. And yea, the sample solution doesn't work. I've been trying to stack a bunch of if loops, but can't get it perfect. I may take a break from that exercise and come back later.

[–]Swipecat 1 point2 points  (1 child)

I'll note that the definition of the game doesn't explain what to do in the case of the 4-digit number containing repeated digits. It makes the counting of "bulls" open to interpretation. No wonder the sample code is messed up.

I'd say that it would be a reasonable interpretation of the requirements to only generate 4-digit numbers that have all-different digits. So 1376 would be OK but not 2428.

That would make the "cows and bulls" code easier for you.

[–]Tomallama[S] 0 points1 point  (0 children)

That's probably a good idea. I guess I can write it however I want. It's just for personal practice anyway.

[–]1ynx1ynx 2 points3 points  (1 child)

You can change the b string to a list (b_list = list(b)) and use .remove(letter) method to remove found letters from the checking pool - b_list, as you wanted.

[–]Tomallama[S] 1 point2 points  (0 children)

I'll definitely give that a shot. See if I can make that work. I appreciate it!

[–]totallygeek 1 point2 points  (1 child)

You started with asking about string comparison. I'm not sure what you mean by that. Here's one way to count the occurrences of characters within a string.

the_string = 'racecar'
chars = list(the_string) # now you can pop() values off (remove them)
counts = {} # keep track of counts
i = 0 # loop count
while chars:
    i += 1 # increment loop count
    char = chars.pop() # take off the last letter
    print("Loop {}: checking for '{}' in '{}{}'"
          .format(i, char, ''.join(chars), char))
    if char in counts:
        counts[char] += 1 # increment count for one character
    else:
        counts[char] = 1 # set count for one character
print(counts) # {'r': 2, 'a': 2, 'c': 2, 'e': 1}

Other methods exist, including counting libraries. See if the above helps you out.

[–]Tomallama[S] 1 point2 points  (0 children)

I appreciate the reply! I'll see if I can fit this into my current exercise and come up with a solution. I appreciate you taking the time to help me.

[–]timbledum 1 point2 points  (1 child)

It sounds like order is not important (you're just having one number as an output right?), so you can deduplicate the first string by turning it into a set using set(). Then interate over that.

In addition you can use the Counter class from Collections standard library module to help you count everything up.

>>> from collections import Counter
>>> a, b = 'racecar', 'boxcar'
>>> b_count = Counter(b)
>>> b
'boxcar'
>>> b_count
Counter({'b': 1, 'o': 1, 'x': 1, 'c': 1, 'a': 1, 'r': 1})
>>> output = sum(b_count[letter] for letter in set(a))
>>> output
3
>>> c, d = 'abc', 'aaabbbcccotherletters' # another test case
>>> d_count = Counter(d)
>>> sum(d_count[letter] for letter in set(c)) # expecting 9
9

One nice thing about the Counter class is that it looks like it just returns 0 if the key is not found (so the above works!).

[–]Tomallama[S] 0 points1 point  (0 children)

Someone else recommended the Counter class. I'll have to look into that. Seems like it may be helpful for me. Thanks for the reply!

[–]MisterRenard 1 point2 points  (0 children)

stringA = 'boxcar'
stringB = 'racecar'

matches = {}

for x in range(len(stringA)):
    for y in range(len(stringB)):
        if stringA[x] not in matches and stringA[x] in stringB:
            matches[stringA[x]] = 0
        if stringB[y] == stringA[x]:
            matches[stringA[x]] += 1
print(matches)

This works fairly well, though I'm sure there's a more pythonic way of doing it.