Python Function: Given Scrabble Tile, What is the value? - python

I'm trying to write a simple function that given a letter, you are returned the value of the scrabble tile. Here is what I have:
def letterPoint(letter):
letter = letter.upper()
lettersWorthOne =(['A','E','I','N','O','R','S','T'])
lettersWorthTwo = (['D','G'])
lettersWorthThree = (['B','C','M','P'])
lettersWorthFour = (['F','H','U','V','W','Y'])
lettersWorthFive = (['K'])
lettersWorthEight = (['J','X'])
lettersWorthTen = (['Q','Z'])
if letterWorthOne:
print '1'
if letterWorthTwo:
print '2'
if letterWorthThree:
print '3'
if letterWorthFour:
print '4'
if letterWorthFive:
print '5'
if letterWorthEight:
print '8'
if letterWorthTen:
print '10'

Use a dictionary. Rather than
lettersWorthTwo = (['D','G']), etc.
You would have a data structure along the lines of:
letterValues = {'D':2, 'G':2, ... }
Then a lookup for value is simply:
letterValues['D'] # returns 2 for the value of the tile

To point out why your code doesn't work, because you're not comparing your letter to the list.
#Change from this:
if letterWorthOne:
print '1'
#to this, should work
if letter in letterWorthOne:
print '1'
.....
Use a python dictionary is the way to go.
Further to the solution someone has already posted. You can also construct a more content dictionary like this:
Letters = {
'a': { 'quantity' : 9, 'value': 1},
'b': { 'quantity' : 2, 'value': 3},
'c': { 'quantity' : 2, 'value': 3},
'd': { 'quantity' : 4, 'value': 2},
'e': { 'quantity' : 12, 'value': 1},
'f': { 'quantity' : 2, 'value': 4},
'g': { 'quantity' : 3, 'value': 2},
'h': { 'quantity' : 2, 'value': 4},
'i': { 'quantity' : 9, 'value': 1},
'j': { 'quantity' : 1, 'value': 8},
'k': { 'quantity' : 1, 'value': 5},
'l': { 'quantity' : 4, 'value': 1},
'm': { 'quantity' : 2, 'value': 3},
'n': { 'quantity' : 6, 'value': 1},
'o': { 'quantity' : 8, 'value': 1},
'p': { 'quantity' : 2, 'value': 3},
'q': { 'quantity' : 1, 'value': 10},
'r': { 'quantity' : 6, 'value': 1},
's': { 'quantity' : 4, 'value': 1},
't': { 'quantity' : 6, 'value': 1},
'u': { 'quantity' : 4, 'value': 1},
'v': { 'quantity' : 2, 'value': 4},
'w': { 'quantity' : 2, 'value': 4},
'x': { 'quantity' : 1, 'value': 8},
'y': { 'quantity' : 2, 'value': 4},
'z': { 'quantity' : 1, 'value': 10},
'*': { 'quantity' : 2, 'value': 0}
}
# to get to it's "content", like this:
Letters['a']
{'quantity': 9, 'value': 1}
# you can then get its 'value' or 'quantity' in a tile bag
Letters['a']['value']
1
# if you MUST use a function, do this with above dictionary, although it's quite pointless
def letter_point(letter):
return Letters[letter.upper()]['value']

In letterPoint(), letterWorthOne and lettersWorthOne are separate variables. Each lettersWorth* variable holds a list, and you appear to want letterWorthOne to contain a boolean value (True or False) specifying whether or not letter is in the lettersWorthOne list. To determine whether a value is in a collection, use operator in.
def letterPoint(letter):
letter = letter.upper()
lettersWorthOne =(['A','E','I','N','O','R','S','T'])
lettersWorthTwo = (['D','G'])
letterWorthOne = letter in lettersWorthOne
if letterWorthOne:
print '1'
letterWorthTwo = letter in lettersWorthTwo
if letterWorthTwo:
print '2'
# rest of values omitted for brevity
print 'E worth'
letterPoint('E')
print 'D worth'
letterPoint('D')
This program produces the following output:
E worth
1
D worth
2
This explains why your existing function doesn't work. But in the long run, I'd recommend using a dictionary to hold the value and quantity of each letter, and store the values as numbers instead of strings so that you can add the values of all letters in a word.

To use the code you began with, you could consider changing the lines to:
if letter in lettersWorthOne print 1
...
This is because the data structure you're using is a list (it is surrounded by [] brackets). The way to use these lists in your function is to see if they contain the letter in the list using the code:
if <variable> in <list> print <value>
The () brackets aren't doing anything, as mentioned in another answer so you can get rid of them.
This is just to explain to you why you don't see any result in the function you've written. The other answers which suggest using a dict (dictionary) are a better approach in practice.

Related

How to iterate dynamically over a dictionary

How to iterate over a dictionary / JSON using a dynamic query.
For example consider the below dict
dict = {'Adam': {
'English': {
'Score': 99,
'Time': 3400,
'Classes': 4},
'Math': {
'Score': 45,
'Time': 779,
'Classes': 5}},
'Tim': {
'English': {
'Score': 74,
'Time': 12,
'Classes': 99},
'Math': {
'Score': 12,
'Time': 333,
'Classes': 1}}
}
I want to set the value of a given path for example
path = '/Adam/English/Score'
new_value = 87
Note that the value assigned could be another dict as well for example
path = '/Adam/English'
new_value = {'Score': 11,
'Time': 2,
'Classes': 9}
Any help would be useful.
Edit: Below is my attempt
keys = path.split('/')[1:]
new_data = None
for key in keys:
if new_data is None:
new_data = dict[key]
else:
new_data = new_data[key]
new_data = new_value
print(dict)
But here the dict still has the old value
I made some assumptions, for example that '/' is not part of any dict-keys and that the path must be valid. Adjust the function as needed.
def deep_set(d, path, value):
sep = '/'
*trail, last = path.strip(sep).split(sep)
for part in trail:
d = d[part]
d[last] = value
Demo:
>>> d = {'a': 1}
>>> deep_set(d, 'a', 2)
>>> d
{'a': 2}
>>> d = {'a': {'b': 1}}
>>> deep_set(d, 'a/b', 2)
>>> d
{'a': {'b': 2}}
edit:
Note that if there are consecutive '/' characters then the empty string will be looked up as a dict key. e.g.
'a/b//c'.split('/') -> ['a', 'b', '', 'c']
It's unclear whether you want to treat leading/trailling '/' characters as part of the path or not (in my function, they are removed with str.strip). Again, adjust as needed.

Removing key from all level of a dictionary in Python

I have a list of dicts. Each dict can be nested. I want to remove the key id from each one of the dics, recursively. Fopr example (Note that I don't know if the amount of levels):
"files" : [
{
'id': 'ada21321',
'd': 'asdasdas',
'data': {
'd': 'asdasdas'
}
},
{
'id': 'ada23112341321',
'd': 'asdasdas',
'data': {
'd': 'asdasdas',
'id': 'asdasd21asda'
}
}
],
I don't know how nested the dics are, and where id is located. I need to remove id from all of the dics from all levels. Output:
"files" : [
{
'd': 'asdasdas',
'data': {
'd': 'asdasdas'
}
},
{
'd': 'asdasdas',
'data': {
'd': 'asdasdas'
}
}
],
I know how to remove in one level:
for current_file in data["files"]:
current_file.pop('id', None)
Is there an elegant way to achieve it?
This should do it for you:
def remove_key(container, key):
if type(container) is dict:
if key in container:
del container[key]
for v in container.values():
remove_key(v, key)
if type(container) is list:
for v in container:
remove_key(v, key)
remove_key(data['files'], 'id')
Output:
{'files': [{'d': 'asdasdas', 'data': {'d': 'asdasdas'}}, {'d': 'asdasdas', 'data': {'d': 'asdasdas'}}]}
You can use recursion:
data = {'files': [{'id': 'ada21321', 'd': 'asdasdas', 'data': {'d': 'asdasdas'}}, {'id': 'ada23112341321', 'd': 'asdasdas', 'data': {'d': 'asdasdas', 'id': 'asdasd21asda'}}]}
def d_rem(d):
if not isinstance(d, dict):
return d if not isinstance(d, list) else list(map(d_rem, d))
return {a:d_rem(b) for a, b in d.items() if a != 'id'}
new_d = d_rem(data)
Output:
{'files': [{'d': 'asdasdas', 'data': {'d': 'asdasdas'}}, {'d': 'asdasdas', 'data': {'d': 'asdasdas'}}]}
This should do the trick (note that this will remove any id keys regardless of whether the associated value to that id key is a str or dict):
def remove_id(file):
for k in list(file.keys()):
if isinstance(file[k], dict):
remove_id(file[k])
if k=='id':
del file[k]
for file in files:
remove_id(file)
Yields:
[{'d': 'asdasdas', 'data': {'d': 'asdasdas'}}, {'d': 'asdasdas', 'data': {'d': 'asdasdas'}}]

cerberus: Validate an optional field occurs at least once

I'm using cerberus to validate data. One of my fields is optional - it doesn't need to be present for every item. However, the key must be populated at least once across the entire data array.
As an example, say I want to validate the key 'c' occurs in at least one dictionary in my data list:
from cerberus import Validator
has_c = {'data': [{'a': 1, 'b': 2}, {'b': 2}, {'c': 3}]}
no_c = {'data': [{'a': 1, 'b': 2}, {'a': 1}]}
schema = {'data':
{'type': 'list',
'schema': {
'type': 'dict',
'schema': {
'a': {'required': True},
'b': {'required': True},
'c': {'required': False, 'at_least_one': True}
}
}
}
}
class MyValidator(Validator) # Some fancy code...
....
v = MyValidator()
v.validate(has_c, schema) # Passes
v.validate(no_c, schema) # Fails
This seems doable outside of cerberus, but I'd like to keep the method in my validator if possible.
If you want the method to be in the Validator subclass, then you will want to create a custom rule just like you were thinking.
from cerberus import Validator
test_with_c = {'data': [{'a': 1, 'b': 2}, {'b': 2}, {'c': 3}]}
test_with_no_c = {'data': [{'a': 1, 'b': 2}, {'a': 1}]}
class MyValidator(Validator):
def _validate_has_c(self, has_c, field, value):
seen_c = False
for v in value:
if "c" in v:
seen_c = True
if has_c and not seen_c:
self._error(field, "Must contain a 'c' key")
schema = {
"data": {
"type": "list",
"has_c": True
}
}
v = MyValidator(schema)
print(v(test_with_c), v.errors)
print(v(test_with_no_c), v.errors)
Running this will yield the results you want with respect to looking for a c key in one of the elements. Running that code yields
True {}
False {'data': ["Must contain a 'c' key"]}

python - Convert list of dicts to hierarchy/multiple nested dicts - issue with orders

Currently I have these input:
query = [{'id': 1, 'desc': 'desc_father', 'parent_id': None}
,{'id': 2, 'desc': 'desc_child_1', 'parent_id': 10}
,{'id': 3, 'desc': 'desc_child_2', 'parent_id': 2}
,{'id': 4, 'desc': 'desc_child_5', 'parent_id': 5}
,{'id': 5, 'desc': 'desc_child_6', 'parent_id': 6}
,{'id': 6, 'desc': 'desc_child_1', 'parent_id': 1}]
This is my recursive function:
def recursive(parent_list, child_dict, parent_id):
for l in parent_list:
if parent_id in l.values():
if 'children' not in l:
l['children'] = []
l['children'].append(child_dict)
break
else:
for i in l:
if isinstance(l[i], list):
recursive(d[i], child_dict, parent_id)
return parent_list
This is my main code:
results = []
for q in query:
dict_item = {}
dict_item['id'] = q['id']
dict_item['desc'] = q['desc']
if q['parent_id'] is None:
results.append(dict_item)
else:
results= recursive(results, dict_item, q['parent_id'])
return results
So, with above given data and the code, I have the result as below:
[{
'desc' : 'desc_father',
'id' : 1,
'children' : [{
'desc' : 'desc_child_1',
'id' : 2,
'children' : [{
'desc' : 'desc_child_2',
'id' : 3
}
]
}, {
'desc' : 'desc_child_1',
'id' : 6
}
]
}
]
This result is as you could see missing the items with id = 4 and id = 5 because during the loops, the parents of these items haven't been created yet (the items with id = 5 & id = 6). I am having difficulties in fixing this problem as I don't know how to traverse back or forward the list to create the father item before the children ones. Help is appreciated. Thanks in advance.
UPDATED
I have added in one case for my query, which is the item with id = 2, this time the item is updated its parent_id to 10 (parent_id = 10), since we do not have the item with id = 10 as parent in our return result, so this id = 2 item will also be a root.
My new code based on Scott Hunter guidance but I still could not make it to work. I must have misunderstood somewhere:
new_dict = {}
for q in query:
q['Children'] = []
new_dict[q['id']] = q
for k, v in new_dict.iteritems():
print k, v
if v['parent_id'] is not None and v['parent_id'] in new_dict:
new_dict[k]['Children'].append(v)
print new_dict
UPDATED-2
Now I make it to work, based on Scott Hunter suggestion, please see my below code. However the code looks ugly with too many for, is there anyway that I could perfect this? Thanks a lot for your support, just one more step and it will be done!
new_dict = {}
for q in query:
q['children'] = []
q['parent'] = 1
new_dict[q['id']] = q
for k, v in new_dict.iteritems():
p_id = v['parent_id']
for kk, vv in new_dict.iteritems():
if kk == p_id:
v['parent'] = 0
vv['children'].append(v)
results = []
for d_id, d_item in new_dict.iteritems():
if d_item['parent'] == 1:
results.append(d_item)
print results
This would be my solution:
#! /usr/bin/env python3
from pprint import pprint
query = [{'id': 1, 'desc': 'desc_father', 'parent_id': None}
,{'id': 2, 'desc': 'desc_child_1', 'parent_id': 1}
,{'id': 3, 'desc': 'desc_child_2', 'parent_id': 2}
,{'id': 4, 'desc': 'desc_child_5', 'parent_id': 5}
,{'id': 5, 'desc': 'desc_child_6', 'parent_id': 6}
,{'id': 6, 'desc': 'desc_child_1', 'parent_id': 1}]
def rec(query, parent):
parent['children'] = []
for item in query:
if item['parent_id'] == parent['id']:
parent['children'].append(item)
rec(query, item)
root = {'id': None}
rec(query, root)
pprint(root, indent=4)
It gives me the output (The keys are out of order, but that's what you get when you use a dictionary)
maurice#ubuntu:~/Dev/random$ python recursion_tree.py
{ 'children': [ { 'children': [ { 'children': [],
'desc': 'desc_child_2',
'id': 3,
'parent_id': 2}],
'desc': 'desc_child_1',
'id': 2,
'parent_id': 1},
{ 'children': [ { 'children': [ { 'children': [ ],
'desc': 'desc_child_5',
'id': 4,
'parent_id': 5}],
'desc': 'desc_child_6',
'id': 5,
'parent_id': 6}],
'desc': 'desc_child_1',
'id': 6,
'parent_id': 1}],
'desc': 'desc_father',
'id': 1,
'parent_id': None}
This should even work with multiple root nodes (there will be a dummy Node with the id None at the top though)
This does not require recursion.
First create a dictionary of nodes, one for each item using id as the key, which includes an empty list of children. Then you can scan that dictionary, and add each node to the list of children for its parent (skipping those whose parent is None). Once this scan is complete, every node that isn't a root will be in the child list of its parent, and thus all trees will be complete.
The roots of the forrest are the nodes that have None for a parent.

Concatenating keys in nested dictionary

Given a nested dictionary:
nested = {
'A': {
'B': {
'C': 'C val',
'G': 'G val'
},
'D': {
'E': {
'F': 'F val'
}
}
}
}
I want to recursively concatenate the keys of the dictionary, except for the "final" key-value pairs, and put the concatenated keys in a new dictionary, like so:
expected = {
'A:B': {'C': 'C val', 'G': 'G val'},
'A:D:E': {'F': 'F val'}
}
How can I make such a function, without knowing the structure of the nested dict beforehand?
A recursive solution is the simplest. This code does as you ask.
def flatten(dictionary, prefix=[], result={}):
for k, v in dictionary.iteritems():
type_v = type(v)
if type_v == dict:
flatten(v, prefix+[k], result)
elif type_v == str:
prefix_str = ':'.join(prefix)
if not prefix_str in result:
result[prefix_str] = {}
result[prefix_str][k] = v
else:
raise TypeError('%s not permissible in data structure' % type_v)
return result
nested = {
'A': {
'B': {
'C': 'C val',
'G': 'G val',
},
'D': {
'E': {
'F': 'F val',
}
}
}
}
expected = flatten(nested)
print(expected)
output
{'A:B': {'C': 'C val', 'G': 'G val'}, 'A:D:E': {'F': 'F val'}}

Categories

Resources