Reading YAML files and accessing lists - python

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))

Related

Python reverse each word in a sentence without inbuilt function python while preserve order

Not allowed to use "Split(),Reverse(),Join() or regexes" or any other
helping inbuilt python function
input something like this:
" my name is scheven "
output like this:
"ym eman si nevehcs"
you need to consider removing the starting,inbetween,ending spaces aswell in the input
I have tried 2 tries, both failed i will share my try to solve this and maby an idea to improve it
First try:
def reverseString(someString):
#lenOfString = len(someString)-1
emptyList = []
for i in range(len(someString)):
emptyList.append(someString[i])
lenOfString = len(emptyList)-1
counter = 0
while counter < lenOfString:
if emptyList[counter] == " ":
counter+=1
if emptyList[lenOfString] == " ":
lenOfString-=1
else:
swappedChar = emptyList[counter]
emptyList[counter] = emptyList[lenOfString]
emptyList[lenOfString] = swappedChar
counter+=1
lenOfString-=1
str_contactantion = ""
#emptyList = emptyList[::-1]
#count_spaces_after_letter=0
for letter in emptyList:
if letter != " ":
str_contactantion+=letter
#str_contactantion+=" "
str_contactantion+=" "
return str_contactantion
second try:
def reverse(array, i, j):
emptyList = []
if (j == i ):
return ""
for k in range(i,j):
emptyList.append(array[k])
start = 0
end = len(emptyList) -1
if start > end: # ensure i <= j
start, end =end, start
while start < end:
emptyList[start], emptyList[end] = emptyList[end], emptyList[start]
start += 1
end -= 1
strconcat=""
for selement in emptyList:
strconcat+=selement
return strconcat
def reverseStr(someStr):
start=0
end=0
help=0
strconcat = ""
empty_list = []
for i in range(len(someStr)):
if(someStr[i] == " "):
continue
else:
start = i
j = start
while someStr[j] != " ":
j+=1
end = j
#if(reverse(someStr,start,end) != ""):
empty_list.append(reverse(someStr,start,end))
empty_list.append(" ")
for selement in empty_list:
strconcat += selement
i = end + 1
return strconcat
print(reverseStr(" my name is scheven "))
The following works without managing indices:
def reverseString(someString):
result = crnt = ""
for c in someString:
if c != " ":
crnt = c + crnt # build the reversed current token
elif crnt: # you only want to do anything for the first space of many
if result:
result += " " # append a space first
result += crnt # append the current token
crnt = "" # and reset it
if crnt:
result += " " + crnt
return result
reverseString(" my name is scheven ")
# 'ym eman si nevehcs'
Try this:
def reverseString(someString):
result = ""
word = ""
for i in (someString + " "):
if i == " ":
if word:
result = result + (result and " ") + word
word = ""
else:
word = i + word
return result
You can then call it like this:
reverseString(" my name is scheven ")
# Output: 'ym eman si nevehcs'
Try this:
string = " my name is scheven "
def reverseString(someString):
result = ''
curr_word = ''
for i in someString:
if i == ' ':
if curr_word:
if result:
result = f'{result} {curr_word}'
else:
result = f'{result}{curr_word}'
curr_word = ''
else:
curr_word = f'{i}{curr_word}'
return result
print(repr(reverseString(string)))
Output:
'ym eman si nevehcs'
Note: if you're allowed to use list.append method, I'd suggest using a collections.deque as it's more performant than appending to a list. But of course, in the end you'll need to join the list together, and you mentioned that you're not allowed to use str.join, so that certainly poses an issue.

Extracting and matching Keys/Values/Words etc from OCR JSON

I'm trying to match KEY_VALUE_SET info to it's real names. This is from Textract already (JSON).
KEY:
KEY_VALUE_SET "Key" VALUE ID > KEY_VALUE_SET "Child" multiple CHILD ID > WORD ID > WORD TEXT
VALUE:
KEY_VALUE_SET "Key" VALUE ID > KEY_VALUE_SET "Value" CHILD ID > WORD ID > WORD TEXT
I'm just starting on this with very basic Python knowledge.
element = ""
childIds = ""
valueIds = ""
keyResult = ""
for item in response["Blocks"]:
if item["BlockType"] == "KEY_VALUE_SET":
if item["Confidence"]:
keyValueSetId = "KeyValueSetID: " + item['Id']
for val in item["EntityTypes"]:
if val == "KEY":
for i in item["Relationships"]:
if i["Type"] == "VALUE":
for i in i["Ids"]:
count = 0
valueId = "ValueId" + str(count) + ": " + i
valueIds += valueId + " "
count +1
elif i["Type"] == "CHILD":
for i in i["Ids"]:
count = 0
childId = "ChildId" + str(count) + ": " + i
childIds += childId + " "
count +1

How to reverse particular string in an input string and return the whole string, with reversed part?

I am having problems with what was said in the title. Basically I am given sentences which contain addresses - I am to reverse only the address in the sentence and return the string. I can reverse the address fine but I am having troubles returning the whole string. My code: ( edit :now corrected):
def problem3(searchstring):
"""
Garble Street name.
:param searchstring: string
:return: string
"""
flag = 0
output = ""
#each word is considered in loop
for i in searchstring.split():
if i.endswith('.'): #if the word ends with .
flag = 0
stype = i
output += " " + stype
elif flag == 1: #if the flag is 1
#street =
output += " " + i[::-1]
elif i.isdigit(): #if the word is digit
flag =1
#num = i
output += i
else:
output += i + " "
#address = num + " " + street + " " + stype
return output
Try this
def problem3(searchstring):
"""
Garble Street name.
:param searchstring: string
:return: string
"""
flag = 0
street = ""
stri=""
#each word is considered in loop
for i in searchstring.split():
if i.endswith('.'): #if the word ends with .
flag = 0
stype = i
continue
if flag == 1: #if the flag is 1
street = street + " " + i[::-1]
continue
if i.isdigit(): #if the word is digit
flag =1
num = i
continue
stri=stri+' '+i
address =stri+" "+ num + " " + street + " " + stype
return address
Then if you call the function:
print(problem3('The EE building is at 465 Northwestern Ave.'))
print(problem3('Meet me at 201 South First St. at noon'))
output will be
The EE building is at 465 nretsewhtroN Ave.
Meet me at at noon 201 htuoS tsriF St.
Instead of using 'if' several times you can use the below code or you can use continue also:
def problem3(searchstring):
"""
Garble Street name.
:param searchstring: string
:return: string
"""
flag = 0
address = ""
#each word is considered in loop
for i in searchstring.split():
if i.endswith('.'): #if the word ends with .
flag = 0
stype = i
address += " " + stype
elif flag == 1: #if the flag is 1
#street =
address += " " + i[::-1]
elif i.isdigit(): #if the word is digit
flag =1
#num = i
address += i
else:
address += i + " "
#address = num + " " + street + " " + stype
return address
print(problem3('The EE building is at 465 Northwestern Ave.'))

Python formatting print

I'm having some formatting issues with my call to print function. For lack of knowledge of better ways to format, i've ended up with an issue. here is what it should look like
However the actual result of my print returns this.
def tupleMaker(inputString):
s1 = inputString.split()
# Adding the surname at the end of the string
s2 = [s1[len(s1) - 1]]
# Number of other names(no surname)
global noOfNames
noOfNames = len(s1) - 4
# Adding all the other names
for i in range(noOfNames):
s2.append((s1[i + 3]))
# Adding the Reg number
s2.append(s1[0])
# Adding the Degree scheme
s2.append(s1[2])
# Adding the year
s2.append("Year " + s1[1])
# Making it a tuple
t = ()
for i in range(len(s2)):
t = t + (s2[i],)
return t
def formatting(t):
s1 = ""
for i in range(len(t)):
s1 += t[i]
if (i == 0):
s1 += ", "
elif (i == len(t) - 4):
s1 += " "
else:
s1 += " "
#print(t[0] + ", ", end="")
#for i in range(noOfNames):
#print (t[i+1], end= " ")
#print(format(t[1+noOfNames], "<32s"))
#print(format(thenames, "<32d") + format(regNo, "<7d") + format(degScheme, ">6s") + format(year, ">1s")
print("")
print(s1)
I would recommend looking at using pythons built in string.format() function a small tutorial is located here: https://pyformat.info/

string index out of range list iteration

I am fairly new to python, I am not sure on how to fix a index string out of range. it happens right after the while loop when I want to send mylist[i][0] to formatting function. Any pointer on my code in general would be awesome!
def formatting(str1):
if str1 == '?':
return True
else:
return False
while(i <= len(mylist)):
val = formatting(mylist[i][0])
if val == True:
str1 = mylist[i]
str2 = mylist[i+1]
i = i + 2
format_set(str1, str2)
else:
if format == True:
if (margin + count + len(mylist[i])) <= width:
if (i == (len(mylist)-1)):
list2.append(mylist[i])
print(" " * margin + " ".join(list2))
break
list2.append(mylist[i])
count += len(mylist[i])
i += 1
else:
print(" " * margin + " ".join(list2))
list2 = []
count = 0
else:
temp_margin = margin
temp_width = width
width = 60
margin = 0
if (margin + count + len(mylist[i])) <= width:
if (i == (len(mylist)-1)):
list2.append(mylist[i])
print(" " * margin + " ".join(list2))
margin = temp_margin
width = temp_width
break
list2.append(mylist[i])
count += len(mylist[i])
i += 1
else:
print(" " * margin + " ".join(list2))
list2 = []
count = 0
change
i <= len(mylist)
to
i < len(mylist)
In the last iteration of the while loop, i is referring to the last value. Hence,
str2 = mylist[i+1]
is trying to reference a string outside the allowed range and you get an error.
EDIT: Also, as Wcrousse mentioned, the while (i <= len(...)) should be changed to i < len(...) because indexes go from 0 - (length-1).

Categories