使用字典过滤对象列表

时间:2013-12-26 15:46:55

标签: python python-2.7 flask

假设我有一个像这样的数据结构(对象列表):

[
 <--
    Name: Bob
    Job: Programmer
    Location: Salem
  -->,
 <--
    Name: Steve
    Job: Sales
    Location: New York
 -->,
 <--
    Name: Jeff
    Job: Programmer
    Location: New York
 -->
]

现在假设我有一个Web表单,它将返回一个字典,其中每个键都将引用该对象中的一个字段。例如,我只想说我得到一本字典:

{"Name": [""], "Job": ["Programmer"], "Location": ["Salem", "New York"]}

这是为了让所有程序员回到Salem和纽约的位置:Bob和Jeff。它将始终是字符串列表的字典。我只想检查对象的字符串值是否包含此字符串。

问题在于迭代字典的每个条目并确保每个对象都匹配字典的所有规范。 这是我当前的代码,它逻辑上只生成包含任何包含任何参数的对象的对象列表,这不是我想要的:

return_list = []
filter_dictionary = dict(request.form)
for row in data:
  for key, value in filter_dictionary.iteritems():
    for obj in value:
      if obj in str(getattr(row, key)):
        return_list.append(row)
return return_list

我确信必须有一个更聪明,更好的方法来做到这一点,也许是涉及到集合的东西。我该怎么做呢?

返回值最终应该是对象列表,如原始示例中所示,但仅包含符合filter_dictionary

所有规范的对象

2 个答案:

答案 0 :(得分:1)

我认为首先你需要改变你的意见:

"Name": [""]建议寻找名称为空字符串的程序员。我认为你应该[]而不是[""],或类似的东西。

鉴于此,这个怎么样?

return_list = []
for row in data:
    if all((getattr(row, k) in v) or (not v) for k, v in filter_dictionary.iteritems()):
        return_list.append(thing)

您当前的代码与此相同:

return_list = []
for row in data:
    if any((getattr(row, k) in v) or (not v) for k, v in filter_dictionary.iteritems()):
        return_list.append(thing)

我认为上述内容或多或少是最好的方式(除了使用数据库)。

但是因为你建议设置......

首先,您需要确保您的数据行中的行支持散列:

class Person:
    def __init__(self, Name, Location, Job):
        self.Name = Name
        self.Location = Location
        self.Job = Job
    def __repr__(self):
        return "Person({}, {}, {})".format(self.Name, self.Location, self.Job)
    def __eq__(self, other):
        return self.Name == other.Name and self.Location == other.Location and self.Job == other.Job
     def __hash__(self):
         return hash(repr(self))

然后这样做:

>>> from operator import itemgetter
>>> from itertools import product
>>> data
[Person(Bob, Salem, Programmer), Person(Steve, New York, Sales), Person(Jeff, New York, Programmer)]
>>> filter_dictionary = {"Name": [], "Job": ["Programmer"], "Location": ["Salem", "New York"]}
>>> fd = {key: (value or [getattr(person, key) for person in data]) for key, value in filter_dictionary.items()}
>>> fd
{'Job': ['Programmer'], 'Location': ['Salem', 'New York'], 'Name': ['Bob', 'Steve', 'Jeff']}
>>> items = list(fd.iteritems())
>>> new = []
>>> for p in product(*map(itemgetter(1), items)):
        temp = {}
        for index, value in enumerate(p):
            temp[items[index][0]] = value
        new.append(temp)

>>> new
[{'Job': 'Programmer', 'Location': 'Salem', 'Name': 'Bob'}, {'Job': 'Programmer', 'Location': 'Salem', 'Name': 'Steve'}, {'Job': 'Programmer', 'Location': 'Salem', 'Name': 'Jeff'}, {'Job': 'Programmer', 'Location': 'New York', 'Name': 'Bob'}, {'Job': 'Programmer', 'Location': 'New York', 'Name': 'Steve'}, {'Job': 'Programmer', 'Location': 'New York', 'Name': 'Jeff'}]
>>> possible_matches = {Person(**kwargs) for kwargs in new}
>>> ts & set(data)
{Person(Bob, Salem, Programmer), Person(Jeff, New York, Programmer)}

正如您所看到的,这更长,更麻烦。我不推荐它。

答案 1 :(得分:1)

其他人可能会提供更简洁的解决方案,但我认为对现有代码的最接近的调整是这样的:

return_list = []
filter_dictionary = dict(request.form)
for row in data:
  match = True # we set to False if any attribute doesn't match
  for key, value in filter_dictionary.items():
    attrmatch = False # we need at least one match per attribute
    for obj in value:
      if obj in str(getattr(row, key)): attrmatch = True
    if not attrmatch: match = False
  if match: return_list.append(row)