简体   繁体   中英

Filtering a dictionary with another dictionary containing multiple items

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM