I have a dictionary that i'd like to filter with another dictionary ('filter options' shown about halfway down this post) with multiple items. I can figure something out for a single item but not the entire dictionary...I've looked about for a while but the other answers are for filter with just one entry (nicely solved with dictionary comprehension)
this is what i have done so far for single item dictionary filter ie
filter_options = {
'Attack': 25}
for kfo, vfo in filter_options.iteritems():
for kp, vp in pokers.iteritems():
if vp[kfo] >= vfo:
print pokedex[kp]
It works but I can't figure it out to filter for multiple items
this is the much-truncated version of the dictionary
pokedex = {1: {'Attack': 49.0,
'Defense': 49.0,
'HP': 45.0,
'Name': 'Bulbasaur',
'PokedexNumber': 1.0,
'SpecialAttack': 65.0,
'SpecialDefense': 65.0,
'Speed': 45.0,
'Total': 318.0,
'Type': 'GrassPoison'},
2: {'Attack': 62.0,
'Defense': 63.0,
'HP': 60.0,
'Name': 'Ivysaur',
'PokedexNumber': 2.0,
'SpecialAttack': 80.0,
'SpecialDefense': 80.0,
'Speed': 60.0,
'Total': 405.0,
'Type': 'GrassPoison'},
3: {'Attack': 82.0,
'Defense': 83.0,
'HP': 80.0,
'Name': 'Venusaur',
'PokedexNumber': 3.0,
'SpecialAttack': 100.0,
'SpecialDefense': 100.0,
'Speed': 80.0,
'Total': 525.0,
'Type': 'GrassPoison'}}
# Only filter based on parameters passed
filter_options = {
'Attack': 25,
'Defense': 30,
'Type': 'Electric'
}
ie Return records with attack >= 25, defense >= 30, and type == "Electric" Also anticipate that other paramters can also be passed such as "SpecialAttack", "Speed", etc.
Example output:
[{'Attack': 30.0,
'Defense': 50.0,
'HP': 40.0,
'Name': 'Voltorb',
'SpecialAttack': 55.0,
'SpecialDefense': 55.0,
'Speed': 100.0,
'Total': 330.0,
'Type': 'Electric'},
{'Attack': 30.0,
'Defense': 33.0,
'HP': 32.0,
'Name': 'Pikachu',
'SpecialAttack': 55.0,
'SpecialDefense': 55.0,
'Speed': 100.0,
'Total': 330.0,
'Type': 'Electric'},
... etc
]
i'll prolly stick it into a function along the lines of
def filtered_pokedex(pokedex_data, filter=filter_options):
....etc
but can sort that out myself
If it needs explaining better or edited just let me know cheers...first ever question on stack exchange so hope I've provided enough info
Cheers
Use all
for this scenario. Make a check to see if the value is a numeric type or string type and alter your condition accordingly.
def foo(vp, k, v):
return vp[k] > v if isinstance(v, (int, float)) else vp[k] == v
for kp, vp in pokedex.iteritems():
if all(foo(vp, k, v) for k, v in filter_options.iteritems()):
print vp
I've defined a function foo
to handle the checking because it tidies up the code.
Here's a Pandas solution:
import pandas as pd
df = pd.DataFrame(pokedex).T
df # change last entry to Type = "Electric" for demo output.
Attack Defense HP Name ... Type
1 49 49 45 Bulbasaur ... GrassPoison
2 62 63 60 Ivysaur ... GrassPoison
3 82 83 80 Venusaur ... Electric
Now build a boolean mask based on filter_options
:
mask = [True] * len(df)
for key in filter_options:
if isinstance(filter_options[key], int):
mask = mask & (df[key] >= filter_options[key]).values
elif isinstance(filter_options[key], str):
mask = mask & (df[key] == filter_options[key]).values
else:
continue
df.loc[mask]
Attack Defense HP Name ... Type
3 82 83 80 Venusaur ... Electric
To answer your question in Python: recursively filter the results of the "single filtering" for each option until the resultant is produced. To make the single filter case fit better, the filter_options is reorganized to contain more information.
But it gets complicated when allowing different operation types. The question does not explicitly ask about this, but does require it beyond the first example. The easiest solution for allowing multiple operation types in a set of filters is a "switch" like construct containing functions for each possible operation, but the "better" solution is to pass the operator itself from the standard operator library.
# pokedex = ...
filter_options = [
{
'attribute': 'Attack',
'operator': '>=',
'value': 25,
},
{
'attribute': 'Defense',
'operator': '>=',
'value': 30,
},
{
'attribute': 'Type',
'operator': '==',
'value': 'Electric',
},
]
# Better to use: https://docs.python.org/2/library/operator.html
operators = {
'<': lambda a, b: a < b,
'>': lambda a, b: a > b,
'==': lambda a, b: a == b,
'<=': lambda a, b: a <= b,
'>=': lambda a, b: a >= b,
}
def filter_single(attribute, operator, value, pokedex=pokedex):
result = {}
for number, pokemon in pokedex.iteritems():
if operators[operator](pokemon[attribute], value):
result[number] = pokemon
return result
def filter(filter_options, pokedex=pokedex):
result = filter_single(
filter_options[0]['attribute'],
filter_options[0]['operator'],
filter_options[0]['value'],
pokedex,
)
for option in filter_options[1:]:
result = filter_single(
option['attribute'],
option['operator'],
option['value'],
result,
)
return result
print filter(filter_options)
This code was tested with Python 3, but should work with 2.7. Replace print
with print()
, and .iteritems()
with .items()
to convert to Python3.
This type of query is trivial to think about with Structured Query Language (SQL). Connecting data structure to thought patterns is one of the purposes of SQL.
Example:
SELECT * FROM pokedex
WHERE attack >= 25
AND defense >= 30
AND type == 'Electric';
PS I think the question's description is missing that "pokers" variable appears to be the attributes available for all pokemon, but that is not needed if it is assumed that filter options will always be valid attribute names. Using a FilterOption class is one way to enforce valid filters.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.