2

I am trying to make a program that counts the occurrences of a letter in a string, but I don't know how to make it case insensitive while preserving the original letter.

I tried assigning each item to a dictionary with the key being the number of occurrences in the string but, if I say for example, abA it will count A and a as different letters.

import operator

def first_non_repeating_letter(string):
    string = string.lower()
    di = {}
    for i in string:
       di[i] = string.count(i)
    if all(value > 1 for value in di.values()):
       return ""
    else:
       var =  min(di.items(), key=operator.itemgetter(1))[0]
       return var

Output: Instead of

output = {"a":1 , b:"1" , "A":1}

I want:

output = {"A/a" : 2, "b":1}

and returning: the repeated letter is A or a

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
Raymoun17
  • 39
  • 7

4 Answers4

2

This will merge all lower/upper case characters and their count to one dictionary:

from collections import Counter
from itertools import groupby

s = 'AbCxbaBc'

out = {}
for v, g in groupby(sorted(Counter(s).items(), key=lambda k: 2*ord(k[0].upper()) - k[0].islower()), lambda k: k[0].lower()):
    l = [*g]
    out[''.join(i[0] for i in l)] = sum(i[1] for i in l)

print(out)

# non repeated letters:

non_repeating = [k for k, v in out.items() if v==1]
print('Non repeated letters:', non_repeating)

# first non repeated letter:

if non_repeating:
    m = min(map(lambda i: (s.index(i), i), non_repeating))
    print('First non repeated letter:', m[-1])
else:
    print('All letters are repeating!')

Prints:

{'aA': 2, 'bB': 3, 'cC': 2, 'x': 1}
Non repeated letters: ['x']
First non repeated letter: x
Andrej Kesely
  • 168,389
  • 15
  • 48
  • 91
  • thats closer to what i want but the problem isnt in counting the letter its in returning the non repeated letter with the original case – Raymoun17 Jul 08 '19 at 16:40
  • @Raymoun17 so return all keys from dictionary `out` where the value is `1` – Andrej Kesely Jul 08 '19 at 16:41
  • but that would also return the repeated letters ex: "sTresS" would return in this case : "T" and "S" but the letter "S" is already repeated in the form of "s" – Raymoun17 Jul 08 '19 at 16:43
  • oh didnt notice – Raymoun17 Jul 08 '19 at 16:44
  • @Raymoun17 No, in this case it will print `['e', 'r', 'T']` as non-repeated letters – Andrej Kesely Jul 08 '19 at 16:44
  • then how do i only return the first one ? should i make a new list out of the previous output ? – Raymoun17 Jul 08 '19 at 16:46
  • Thank you very much it worked but what if the string doesnt contain any repeated letter how do i return none or "no unique letter" ? – Raymoun17 Jul 08 '19 at 17:00
  • @Raymoun17 Now in my code I just print `print('All letters are repeating!')`, so in your code you should probably return or set some variable, maybe raise Exception. depends on your case. – Andrej Kesely Jul 08 '19 at 17:03
2

This should work for what you need it to.

import operator

def first_non_repeating_letter(string):
    dict = {}
    lowercase_string = string.lower()
    # first we have to make a dictionary counting how many occurrences there are
    for ch in lowercase_string:
        if ch in dict:
            dict[ch] = dict[ch] + 1
        else:
            dict[ch] = 1

    # check if the number of occurrences is one, then return it
    for ch in lowercase_string:
        if ch in dict and dict[ch] == 1:
            index = lowercase_string.find(ch)
            return string[index]

Example:

Input: "sTreSs"

Output: "T"

Josh Correia
  • 3,807
  • 3
  • 33
  • 50
1

This may help

import collections
c = collections.Counter('PaRrOt'.lower())
sorted(c.items(), key=lambda c: c[0])
[('a', 1), ('o', 1), ('p', 1), ('r', 2), ('t', 1)]

Source https://stackoverflow.com/a/22819467/5202279

EDIT: Based on OP's comments.

You can simply check if the dictionary value for that particular alphabet is 1 then it is non-repeating.

As for particular_case of alphabet. see if the alphabet is non repeating than match it with regex to original string.

WhySoSerious
  • 185
  • 1
  • 19
1

You could always create a pair of mappings: one of the case-folded characters to their counts, and one to their first occurrence.

from collections import Counter

some_string = 'Some string'
lower_string = some_string.casefold()
count = Counter(lower_string)
firsts = dict(zip(lower_string, some_string))

Now, for and character in count, you can look up its original appearance in firsts.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264