`SyntaxError` in if-else one-liner - python

I wrote this block of code, but it doesn't work because of SyntaxError.
def char_freq(message):
d = dict()
for ch in message:
d[ch] += 1 if ch in d else d[ch] = 1
return d ^ SyntaxError: End of statement expected
I don't know how to rewrite the expression in order to keep if-else in one line and to get it to work.
I know it is possible to implement the function as a simple for loop, but I don't understand, why my if-else one-liner results in SyntaxError?

Turn d into a defaultdict and then you can just ignore the statement altogether
from collections import defaultdict
def char_freq(message):
d = defaultdict(int)
for ch in message:
d[ch] += 1
return d
It also looks like you're just counting characters so you could just use a counter
from collections import Counter
def char_freq(message):
return Counter(message)

As you asked to keep the if/else Do
d[ch] = (d[ch] + 1) if ch in d else 1
But the dict.get() syntax is nicer d[ch] = d.get(ch, 0) + 1
Or a collections.defaultdict with int factory
def char_freq(message):
d = defaultdict(int)
for ch in message:
d[ch] += 1
return d

Related

Use a dictionary to calculate most common letter

I am trying to write a function that will take in a string and use a dictionary to calculate and return the most common letter in that string. I believe that my code is close to working; however, I get a "cant assign to function call" error on line 5.
Here is my code so far:
def mostCommon(myString):
charCount = []
for c in myString.lower():
if c in charCount:
charCount(c) += 1
else:
charCount(c) = 1
myVal = 0
myKey = 0
for key, value in charCount.lower():
if value > myVal:
myVal = value
myKey = key
return charCount
Here's your function with the errors corrected.
def mostCommon(myString):
charCount = {}
for c in myString.lower():
if c in charCount:
charCount[c] += 1
else:
charCount[c] = 1
myVal = 0
myKey = 0
for key, value in charCount.items():
if value > myVal:
myVal = value
myKey = key
return myKey
Here's a much simpler way of doing it
from collections import Counter
def mostCommon(myString):
return Counter(myString).most_common(1)[0][0]
Well you defined charCount as a list, and then tried to call it like a function. If you want charCount to just be a number, just set it to 0 before your for loop.
Or to use a dict
charCount = {}
for c in myString.lower():
if c in charCount:
charCount[c] += 1
I think you intended charCount to be a dict not a list. Here is a simple solution using the max function:
def mostCommon2(myString):
charCount = {}
for c in myString.lower():
if c in charCount:
charCount[c] += 1
else:
charCount[c] = 1
return max(charCount, key=charCount.get)
here after a few things that can help.
The correct syntax to declare a dictionary is charCount = {}
you cannot create an item with charCount(c), you better do charcount[c] = 'c'
To add element to a dictionnary: Add new keys to a dictionary?

Count character occurrences in a Python string

I want to get the each character count in a given sentence. I tried the below code and I got the count of every character but it displays the repeated characters count in the output. How to delete repeated character.
def countwords(x):
x=x.lower()
for i in x:
print(i,'in',x.count(i))
x=str(input("Enter a paragraph "))
countwords(x)
My output is:
My output should not contain spaces count and repeated characters.. What to do....!!!
check this code :
my_string = "count a character occurance"
my_list = list(my_string)
print (my_list)
get_unique_char = set(my_list)
print (get_unique_char)
for key in get_unique_char:
print (key, my_string.count(key))
There are a few different approaches, most hinted at by jonrsharpe's comment, but I'd suggest a simple set.
The set approach, along with a few others are included below:
# An approach using a set
def countwords_set(s):
for c in set(s):
if c == ' ': continue
print(c, 'in', s.count(c))
# An approach using a standard dict
def countwords_dict(s):
d = dict()
for c in s:
if c == ' ': continue # Skip spaces
d[c] = d.get(c,0) + 1 # Use the .get method in case the
# key isn't set
for c,x in d.items(): # Display results
print(c, 'in', x)
# An approach using a defaultdict (from the collections module)
def countwords_ddict(s):
from collections import defaultdict # Typically, imports go at the top
d = defaultdict(int)
for c in s:
if c == ' ': continue
d[c] += 1
for c,x in d.items():
print(c, 'in', x)
# An approach using a Counter (from the collections module)
def countwords_counter(s):
from collections import Counter # Typically, imports go at the top
counter = Counter(s)
# Counters can be accessed like dicts
for c,x in counter.items():
if c == ' ': continue
print(c, 'in', x)
# User input and comparison
s = str(input("Enter a paragraph "))
s = s.lower()
countwords_set(s)
print("---")
countwords_dict(s)
print("---")
countwords_ddict(s)
print("---")
countwords_counter(s)
print("---")
The output is the essentially the same for each approach, although the order of the characters may differ since Python dictionaries are unordered.
Use a dict.
def countwords(x):
d = dict()
x=x.lower()
for i in x:
if i in d.keys():
d[i] = d[i] +1;
else:
d[i] = 1;
for i in d.keys():
print i + " " + d[i]
Or use numpy with this one-liner
np.unique(np.array(list('count these characters')), return_counts=True)

I have a program to find the amount of alphabets in a string but its not complete can you complete it python

def encode(source):
dest="";
i=0
while i<len(source):
runLength = 1;
while source[runLength] == source[runLength-1]:
runLength=runLength+1
i=i+1
dest+=source[i]
dest+=str(runLength)
i=i+1
return dest
source = raw_input("")
print (encode(source))
sample input:
AABBCCCCDADD
sample output:
3A2B4C3D
please fix it, mostly changing line 6 should do it I think
You can simply do it using dictionary.
x="AABBCCCCDDD"
d={}
for i in x:
d.setdefault(i,0)
d[i]=d[i]+1
print "".join([str(j)+i for i,j in d.items()])
The best way is to use a dict to keep the count, an OrderedDict will also keep the order for you:
from collections import OrderedDict
def encode(source):
od = OrderedDict()
# iterate over input string
for ch in source:
# create key/value pairing if key not alread in dict
od.setdefault(ch,0)
# increase count by one each time we see the char/key
od[ch] += 1
# join the key/char and the value/count for each char
return "".join([str(v)+k for k,v in od.items()])
source = "AABBCCCCDDD"
print (encode(source))
This will only work for strings like your input where the chars don't repeat later, if they do we can keep track in a loop and reset the count when we meet a char that was not the same as he previous:
def encode(source):
it = iter(source)
# set prev to first char from source
prev = next(it)
count = 1
out = ""
for ch in it:
# if prev and char are equal add 1 to count
if prev == ch:
count += 1
# else we don't have sequence so add count and prev char to output string
# and reset count to 1
else:
out += prev + str(count)
count = 1
prev = ch
# catch out last match or a single string
out += prev + str(count)
return out
Output:
In [7]: source = "AABBCCCCDDDEE"
In [8]: print (encode(source))
A2B2C4D3E2
As an alternative solution, there is a Python library called itertools that has a function which is useful in this situation. It can split your string up into groups of the same letter.
import itertools
def encode(source):
return "".join(["%u%s" % (len(list(g)), k) for k,g in itertools.groupby(source)])
print encode("AABBCCCCDDD")
This will print out the following:
2A2B4C3D
To see how this works, see the following smaller version:
for k, g in itertools.groupby("AABBCCCCDDD"):
print k, list(g)
This prints the following:
A ['A', 'A']
B ['B', 'B']
C ['C', 'C', 'C', 'C']
D ['D', 'D', 'D']
You can see k is the key, and g is the group. If we take the length of each group, you have your solution.

An alternative to pythons "setdefault" augmented assignment

I want a dictionary that shows boolean counts. I.e. how often name/position combination meets criteria. E.g.:
Key - Value1 - Value2
John12 Yes:300 No:25
John13 Yes:400 No:29
Linda13 Yes:300 No:60
...
I tried this:
if str(f[1]) + str(f[7]) in psHpGrp:
if f[6] == 1:
psHpGrp.setdefault(str(f[1]) + str(f[7]), []) +=1
And because of a bug I got "SyntaxError: illegal expression for augmented assignment"
So googling gave me this:
if str(f[1]) + str(f[7]) in psHpGrp:
if f[6] == 1:
i = psHpGrp.setdefault((f[1]) + str(f[7]), [])
i += 1
else:
j = psHpGrp.setdefault((f[1]) + str(f[7]), [])
j += 1
else:
psHpGrp.setdefault(str(f[1]) + str(f[7]), []).append(str(f[1]) + str(f[7]))
And now I get: j += 1 'int' object is not iterable
Whats wrong here?
You want to use defaultdict:
>>> from collections import defaultdict
>>> d = defaultdict(int)
>>> d['a'] += 1
>>> d['b'] += 1
>>> d['b'] += 1
>>> print d['a'], d['b'], d['c']
1 2 0
from collections import Counter
psHpGrp.setdefault(str(f[1]) + str(f[7]), Counter()).update([f[6] == 1])
The first part:
psHpGrp.setdefault(str(f[1]) + str(f[7]), Counter())
will take the object for the key str(f[1]) + str(f[7]) from the dictionary psHpGrp and if it is not present, create a new Counter.
Then it will .update([f[6] == 1]) it with the result of the condition f[6] == 1, which can be True or False. The Counter holds then the number of Trues and Falses as a dictionary. They represent your "Yes"/"No", just they are booleans.
like #larsmans said, you can't add an int to a list (using +=). On your initial attempt replace [] with 0, and then increment the number for that entry.
if str(f[1]) + str(f[7]) in psHpGrp:
if f[6] == 1:
psHpKey = str(f[1]) + str(f[7])
psHpGrp.setdefault(psHpKey, 0)
psHpGrp.setdefault[psHpKey] +=1
Also: Your final error seems to be arising from code that you have not posted. Python lets you know on which line the offending code is, it's best to post at least that line of code.

Assigning value in a while comparison expression

Hi I was wondering if it was possible to assign a value in the while comparison section of the code.
Here is an example of the code currently
startIndex = find(target, key, startIndex)
while( startIndex != -1):
matchesFound += 1
startIndex = find(target, key, startIndex + 1)
return matchesFound
What I want to do is move the startIndex = find(target, key, startIndex) into the while comparison expresion so it would look something like this
while( (startIndex = find(target, key, startIndex)) != -1):
matchesFound += 1
startIndex + 1
return matchesFound
if not, what would a better refactor be?
Thanks
edit
I'm working through the MIT Open courseware 6.00 before I try out the famous 6.001 module
If for some reason you can't use a more appropriate method from whatever you're searching, this is equivalent to your working code from the question:
start = 0
count = 0
for match in iter(lambda: find(target, key, start), -1):
count += 1
start = match + 1
return count
However, you'll get the most mileage out of writing an iterator for finding these matches, "transforming" your current find function:
def findall(target, key, start=0):
for match in iter(lambda: find(target, key, start), -1):
yield match
start = match + 1
Then count from that:
count = sum(1 for m in findall(target, key))
You're writing C in Python.
Try:
startIndex = -1
while True:
startIndex = find(target, key, startIndex + 1)
if startIndex < 0:
break
matchesFound += 1
return matchesFound
Or perhaps even:
return target.count(key)
With the PEP 572 being accepted, it will be possible to do this:
while (startIndex := find(target, key, startIndex)) != -1:
matchesFound += 1
startIndex + 1
return matchesFound
It will be included in Python 3.8.
This change will make python code even smaller. Consider the following standart library code:
while True:
line = fp.readline()
if not line:
break
m = define_rx.match(line)
if m:
n, v = m.group(1, 2)
try:
v = int(v)
except ValueError:
pass
vars[n] = v
else:
m = undef_rx.match(line)
if m:
vars[m.group(1)] = 0
It will be improved like this:
while line := fp.readline():
if m := define_rx.match(line):
n, v = m.group(1, 2)
try:
v = int(v)
except ValueError:
pass
vars[n] = v
elif m := undef_rx.match(line):
vars[m.group(1)] = 0
No, you can't do that in Python. I think the main reason Python disallows this, is to avoid the frequent errors resulting from confusing assignment and equality checking.
Python claims to have readable code as a main guideline, so I think your original code is fine. No need to refactor...
i would do it like this
startIndex=0
while 1:
startIndex = find(target, key, startIndex+1)
if startIndex == -1: break
matchesFound += 1
you can put in more conditions inside the while loop like that.
Edit: #OP, in future, to count match of string, just use count
>>> mystring = "abc defabc fgh ijkabc blah"
>>> mystring.count("abc")
3
>>>
Edit.
We refactor it like this.
matches = [ k for k in range(len(target)-len(key)) if target[k:].startswith(key) ]
matchesFound = len(matches)
We don't need C-style condition and assignment conflation.
Rarely do you simply want the count; the actual locations are available for free.

Categories

Resources