I am trying to merge variables to keys existing in a dictionary.
Here are the rules:
If key already exists, then increment the value counter by one.
If partial match to key exists:
a. If variable length is smaller than key but re.search is not None, increment the value counter by one
b. If variable length is larger than key and re.search is not None, replace key by variable and increment counter by one
If variable exists after this but still has no match in dictionary, add variable to dictionary
I have been able to accomplish 1, 2a and 2b but I am not sure how to add 3. Any help/suggesstions will be appreciated.
Here is the script in its present form: I would also like to see "turtle" in the Dict.
Animals = ["phant", "eleph", "tiger", "turtle", "zebra", "ostrich"]
Dict = {"horse":1, "elephant":1, "iger":1, "ostrich":1}
for name in Animals:
if name in Dict:
Dict[name]=Dict[name]+1
else:
for key, val in Dict.items():
if len(name) < len(key):
m = re.search (name, key)
if m != None:
print ("Found match!", name)
Dict[key] = Dict[key] + 1
break
elif len(name) > len(key):
m = re.search (key, name)
if m != None:
print ("Found match!", name)
Dict[name] = Dict.pop(key) + 1
Dict[name] = Dict[name] + 1
break
One way to describe case #3 after looking at the code is "if neither of the break statements were executed". You can handle this case by putting an else statement after the for loop. The code within this block will only be executed if the for loop ran to completion (or in other words, there was no break statement run within the loop):
Animals = ["phant", "eleph", "tiger", "turtle", "zebra", "ostrich"]
Dict = {"horse":1, "elephant":1, "iger":1, "ostrich":1}
for name in Animals:
if name in Dict:
Dict[name]=Dict[name]+1
else:
for key, val in Dict.items():
if len(name) < len(key):
m = re.search (name, key)
if m != None:
print ("Found match!", name)
Dict[key] = Dict[key] + 1
break
elif len(name) > len(key):
m = re.search (key, name)
if m != None:
print ("Found match!", name)
Dict[name] = Dict.pop(key) + 1
Dict[name] = Dict[name] + 1
break
else: # this line and the one below it are new
Dict[name] = 1
for name in Animals:
if name in Dict:
Dict[name]=Dict[name]+1
else:
found= False
for key, val in Dict.items():
if len(name) < len(key):
m = re.search (name, key)
if m != None:
print ("Found match!", name)
Dict[key] = Dict[key] + 1
found= True
break
elif len(name) > len(key):
m = re.search (key, name)
if m != None:
print ("Found match!", name)
Dict[name] = Dict.pop(key) + 1
found= True
break
if not found
Dict[name] = 1
I'd prefer the following, though:
for name in Animals:
if name in Dict:
Dict[name]=Dict[name]+1
else:
found= False
for key, val in Dict.items():
m = re.search (name, key)
if m != None:
print ("Found match!", name)
if len(name) < len(key):
Dict[key] = Dict[key] + 1
else
Dict[name] = Dict.pop(key) + 1
found= True
break
if not found
Dict[name] = 1
Related
I want to make a program that adds a name in the dictionary if it doesn't exist already and count the times it is given as input. My code works, however, it doesn't add 1 when it iterates.
namelist = {}
def namen():
while True:
word = input('Vul een naam in: ')
if word == '':
break
else:
for name in namelist:
if word == name:
namelist[word] += 1
# else wasn't properly indented earlier
else:
namelist[word] = 1
print(namen())
print(namelist)
You can use the dict.get method instead to provide a default value to a new entry to the dict:
namelist = {}
def namen():
while True:
word = input('Vul een naam in: ')
if word == '':
break
else:
for name in namelist:
if word == name:
namelist[word] = namelist.get(word, 0) + 1
Your check is incorrect, you need if rather than for to see if the key exists, then you can remove the inner if statement
if name in namelist:
namelist[word] += 1
else:
namelist[word] = 1
No one said anything about the has_key method of dictionaries, which is in my opinion the standard way to to this:
namelist = {}
def namen():
while True:
word = input('Vul een naam in: ')
if word == '':
break
else:
if namelist.has_key(word):
namelist[word] += 1
else:
namelist[word] = 1
print(namen())
print(namelist)
try this
namelist = {}
def namen():
while True:
word = input('Vul een naam in: ')
if word == '':
break
else:
try:
namelist[word] += 1
except:
namelist[word] = 1
print(namen())
print(namelist)
I am currently reading data from a .yml file. Inside the file is the following part for every main entry:
- !
name: Martial Focus
prerequisites:
tier1:
any:
Attribute:
- Attribute1:§ 1
- Attribute2:§ 1
Feat:
- Feat1
Other:
- Other Prerequisites
cost:
- 3
description: |
[...]
effect: |
[...]
I've been able to read all the data, including 'prerequisites', but here I have a special problem:
Where with the other data, I was able to access sublists it seems to be different for this:
The "any:" part is optional, so it could also say something like
prerequisites:
tier1:
Attribute:
- Attribute1:§ 1
- Attribute2:§ 1
Feat:
- Feat1
Other:
- Other Prerequisites
Reading the .yml file converts the part above to
'prerequisites': {
'tier1': {
'any': {
'Attribute': ['Attribute1:§ 1', 'Attribute2:§ 1'],
'Feat': ['Feat1'],
'Other': ['Other Prerequisites']
}
}
}
So in my code, for every "tierX", I check if it contains a key "any:" via
if 'any' in tier:
# do the stuff to be done if 'any' exists
else:
# do the stuff to be done if it doesn't
But it never seems to be true. Since "Attribute:", "Feat:" and "Other:" are also optional, I do the same for those inside the if-else-statement and it's the same problem with them though for those there's no else-statement.
Below you can find the code I'm using. It won't be the prettiest since I litterally started with python today but I hope that you'll help me anyway:
prerequisites = ""
tierNum = 0
for tier in data['prerequisites']:
tierNum += 1
thisTier = ""
if 'any' in tier:
print("'any' found!")
content = tier['any']
if 'Other' in content:
other = ""
for s2 in content['Other'][:-1]:
other += s2 + ", "
thisTier += "**" + other
if len(content['Other'][:-1]) == 0:
thisTier += str(content['Other'][-1:])
else:
thisTier += "or " + str(content['Other'][-1:])
if 'Attribute' in content:
attributes = ""
for s2 in content['Attribute'][:-1]:
attributes += s2 + ", "
if thisTier.length() == 0:
thisTier += "**" + attributes
else:
thisTier += ", or " + attributes
if len(content['Attribute'][:-1]) == 0:
thisTier += str(content['Attribute'][-1:])
else:
thisTier += "or " + str(content['Attribute'][-1:])
if 'Feat' in content:
feats = ""
for s2 in content['Feat'][:-1]:
feats += s2 + ", "
if thisTier.length() == 0:
thisTier += "**" + feats
else:
thisTier += ", or " + feats
if len(content['Feat'][:-1]) == 0:
thisTier += str(content['Feat'][-1:])
else:
thisTier += "or " + str(content['Feat'][-1:])
else:
content = tier
if 'Other' in content:
other = ""
for s2 in content['Other'][:-1]:
other += s2 + ", "
thisTier += "**" + other
if len(content['Other'][:-1]) == 0:
thisTier += str(content['Other'][-1:])
else:
thisTier += "or " + str(content['Other'][-1:])
if 'Attribute' in content:
attributes = ""
for s2 in content['Attribute'][:-1]:
attributes += s2 + ", "
thisTier += "**" + attributes
if len(content['Attribute'][:-1]) == 0:
thisTier += str(content['Attribute'][-1:])
else:
thisTier += "or " + str(content['Attribute'][-1:])
if 'Feat' in content:
feats = ""
for s2 in content['Feat'][:-1]:
feats += s2 + ", "
thisTier += "**" + feats
if len(content['Feat'][:-1]) == 0:
thisTier += str(content['Feat'][-1:])
else:
thisTier += "or " + str(content['Feat'][-1:])
prerequisites += "*Tier {0}:\n{1}\n".format(tierNum, thisTier)
prerequisites = prerequisites[:-1]
I'm doing stuff like the content['Feat'][:-1] in order to get every element except the last so I can add a ", or " in front of the last element, should there be more than one.
EDIT:
My desired Output would be something like:
Prerequisites:
*Tier 1:
**Attribute1 1, or Attribute2 1
**Feat1
**Other Prerequisites
If no any exists and
Prerequisites:
*Tier 1:
**Attribute1 1, or Attribute2 1, or Feat1, or Other Prerequisites
if it doesn't
Your problem is that for tier in data["predicates"] iterates over the keys of the predicate dictionary, thus the subsequent if "any" in tier actually evaluates "any" in "tier1" which is of cause always false.
What you want to test here is "any" in data["predicates"]["tier1"]. When working with dictionaries (i.e. mappings) you have to differentiate between a key and its corresponding value.
Interestingly you have gotten it right for the next level down:
# ...
content = tier['any']
if 'Other' in content:
other = ""
for s2 in content['Other']:
# ...
Ways to iterate over a dictionary
d = {"key1":"value1", "key2":"value2", "key3":"value3"}
for key in d:
print(key)
# prints key1, key2, key3
for key in d.keys():
print(key)
# prints key1, key2, key3
for value in d.values():
print(value)
# prints value1, value2, value3
for item in d.items():
print(item)
# prints (key1,value1), (key2,value2), (key3,value3)
for key, value in d.items():
print(key)
print(value)
# prints key1, value1, key2, value2, key3, value3
see python documentation here and here
As you are new to Python and do not know what is possible, allow me to present you a much more elegant solution with out all the repetitive string operations:
import yaml
yamldata1 = r"""
- !
name: Martial Focus
prerequisites:
tier1:
any:
Attribute:
- Attribute1:§ 1
- Attribute2:§ 1
Feat:
- Feat1
Other:
- Other Prerequisites
cost:
- 3
description: |
[...]
effect: |
[...]
"""
yamldata2 = r"""
- !
name: Martial Focus
prerequisites:
tier1:
Attribute:
- Attribute1:§ 1
- Attribute2:§ 1
Feat:
- Feat1
Other:
- Other Prerequisites
cost:
- 3
description: |
[...]
effect: |
[...]
"""
def process(data):
output = ""
for tier_name, tier in data['prerequisites'].items():
output += f"* {tier_name}"
if 'any' in tier:
content = tier['any']
prerequisites = content.get('Other', []) + content.get('Attribute', []) + content.get('Feat', [])
if prerequisites:
output += "\n** " + " or ".join(prerequisites)
else:
content = tier
prerequisites = [content.get('Other', []), content.get('Attribute', []), content.get('Feat', [])]
for subset in prerequisites:
if subset:
output += "\n** " + " or ".join(subset)
return output
data = yaml.load(yamldata1)[0]
print(process(data))
print('#'*10)
data = yaml.load(yamldata2)[0]
print(process(data))
IDnum = input("\nprompt: ")
if int(IDnum) >= 0 :
if int(IDnum) in T.keys() :
print("ID number(s) that {} will contact is(are) {}.".format(int(IDnum),T[int(IDnum)]))
else :
print("Entered ID number {} does not exist.".format(int(IDnum)))
else:
break
It's actually a while loop, receiving ID numbers and checking whether the numbers are in the file.
I'd like to make it discern whether the input is an integer >= 0 and if it's anything else, (eg. space,enter,characters,float,etc) break the loop.
How can I do this using if statements?
I have tried
if IDnum == '' or IDnum == ' ' or int(IDnum) < 0 :
but as you know, it cannot cover all the other cases.
T = {1: 1, 2: 2}
while True:
IDnum = input("\nprompt: ")
try:
num = int(IDnum)
if num < 0:
raise ValueError('Negative Integers not allowed')
except ValueError: # parsing a non-integer will result in exception
print("{} is not a valid positive integer.".format(IDnum))
break
if num in T:
print("ID number(s) that {} will contact is(are) {}.".format(num,T[num]))
else:
print("Entered ID number {} does not exist.".format(num))
Thanks to #adirio and #moses-koledoye for the suggested improvements.
Do the check with a try-except statement.
def is_pos_int(IDnum):
''' Check if string contains non-negative integer '''
try:
number = int(IDnum)
except ValueError:
return False
if number >= 0:
return True
else:
return False
For example
is_pos_int('1 ') # notice the space
Out[12]: True
is_pos_int('-1')
Out[13]: False
is_pos_int('1.0')
Out[15]: False
is_pos_int('word')
Out[16]: False
Then:
while True:
if not is_pos_int(IDnum):
break
else:
val = int(IDnum)
if val in T.keys() :
print("ID number(s) that {} will contact is(are) {}.".format(val, T[val]))
else :
print("Entered ID number {} does not exist.".format(val))
The function append below takes a pair which is an immutable tuple as a parameter. In the processing of the append it is necessary to enclose all values with start and end single quotes. Because the tuple values are immutable I cannot simply do this:
if item[1][0] != "'" and item[1][-1] != "'":
item[1] = "'{0}'".format(item[1])
self.keyvalues[item[0]] = item[1]
Hence the handling as follows:
if item[1][0] != "'" and item[1][-1] != "'":
self.keyvalues[item[0]] = "'{0}'".format(item[1])
else:
self.keyvalues[item[0]] = item[1]
Full code appears below.
Is there a more elegant way to add the key and value to the dictionary.
class section(object):
"""class to hold a section. name is name of section. keyvalues is a key
value dictionary of keys and values"""
def __init__(self, name):
self.name = name
self.keyvalues = {}
print "Constructed section:", name
def append(self, item):
"""add a key value pair to section"""
if len(item) != 2:
return False
else:
print "adding:", item, "to", self.name, "object"
# cannot do this because tuple is immutable
#item[1] = "'{0}'".format(item[1])
# Would there be a more elegant way of doing this - given that
# parameter must be a immutable tuple?
if item[1][0] != "'" and item[1][-1] != "'":
self.keyvalues[item[0]] = "'{0}'".format(item[1])
else:
self.keyvalues[item[0]] = item[1]
return True
def __repr__(self):
s = "contains\n"
for k, v in self.keyvalues.iteritems():
s += "\t{0}={1}\n".format(k, v)
return s
__str__ = __repr__
mysection = section("section1")
dnpair = ("key1", "value1")
mysection.append(dnpair)
print mysection
Since the tuple is not used again, just split it to separate variables.
key, value = item
if value[0] != "'" and value[-1] != "'":
value = "'{0}'".format(value)
self.keyvalues[key] = value
Elegant is subjective, but this is the most elegant solution I could come up with. It allows value manipulation by passing it off to a function. Also the ugly
if item[1][0] != "'" and item[1][-1] != "'":
becomes
if not value[0] == value[-1] == "'":
Here's the full code with the modified append and newly created quote_wrap method.
def append(self, item):
"""add a key value pair to section"""
if len(item) != 2:
return False
else:
print( "adding:", item, "to", self.name, "object")
self.keyvalues[item[0]] = self.quote_wrap(item[1])
return True
def quote_wrap(self, value):
if not value[0] == value[-1] == "'":
value = "'{}'".format(value)
return value
def main():
names=[0]*10
for index in range(len(names)):
names[index] = input("Enter word " + str(index + 1) + ": ")
bubbleSort(names)
print("Names in Alphabetical order:")
print(names)
def bubbleSort(names):
for maxElement in range(len(names)-1, 0, -1):
for index in range(maxElement):
if names[index] > names[index+1]:
temp = names[index]
names[index] = names[index+1]
names[index+1] = temp
found = False
index=0
while found == False and index < len(names):
Searchword= input('enter a searchword:')
if scores[index] == Searchword :
found = True
else:
index = index + 1
if found:
print("Found")
else:
print("Not Found")
main()
Does everything required accept when a Searchword is entered that cannot be found it does not print 'not found' but only keeps asking for input.
Change if scores[index] == Searchword : to if names[index] == Searchword :
Place Searchword= input('enter a searchword:') outside the while loop
It should look something like this:
def main():
names=[0]*10
for index in range(len(names)):
names[index] = input("Enter word " + str(index + 1) + ": ")
bubbleSort(names)
print("Names in Alphabetical order:")
print(names)
def bubbleSort(names):
for maxElement in range(len(names)-1, 0, -1):
for index in range(maxElement):
if names[index] > names[index+1]:
temp = names[index]
names[index] = names[index+1]
names[index+1] = temp
found = False
index=0
Searchword= input('enter a searchword:')
while found == False and index < len(names):
if names[index] == Searchword :
found = True
else:
index = index + 1
if found:
print("Found")
else:
print("Not Found")
main()
you might need input before the loop, ie:
Searchword= input('enter a searchword:')
while found == False and index < len(names):
if scores[index] == Searchword :
found = True
else:
index = index + 1