Say for an example, I've a list like this:
q_list = [('94.vmtest2', 'sgmops', 'Andy Hays', '27/04 00:27:26', 'C', '27/04 00:28:31', 'vmtest1.hep', 'express', '00:00:10', '00:01:04'),
('96.vmtest2', 'sgmops', 'John Dee', '27/04 01:27:26', 'C', '27/04 01:28:33', 'vmtest1.hep', 'short', '00:00:09', '00:01:06'),
('99.vmtest2', 'sgmops', 'Andy Hays', '27/04 07:19:56', 'C', '27/04 07:21:12', 'vmtest1.hep', 'express', '00:00:10', '00:01:14'),
('103.vmtest2', 'sgmops', 'John Dee', '27/04 14:08:00', 'C', '27/04 14:09:16', 'vmtest1.hep', 'express', '00:00:10', '00:01:16'),
('102.vmtest2', 'sgmops', 'John Dee', '27/04 14:02:38', 'C', '27/04 14:10:12', 'vmtest1.hep', 'short', '00:00:10', '00:01:10')]
which is formed from the log files. and then I have a dicti开发者_如何学编程onary like this:
q_dict = {'username': 'johndee', 'queue': 'short'}
which is formed from a query string (the user input). What I want is to print [first 8 items of] the only lines from the list match with the value in the dictionary. In this case, I'll only print these two lines:
96.vmtest2 sgmops John Dee 27/04 01:27:26 C 27/04 01:28:33 vmtest1.hep short
102.vmtest2 sgmops John Dee 27/04 14:02:38 C 27/04 14:10:12 vmtest1.hep short
In fact it doesn't have to be a dictionary at all; the user input (command-line argument) is like this:
'formatDate(%m/%d) == 4/27 && username == John Dee && queue == short'
and I'm creating the dictionary out of that. Any idea how can I do that? Thanks in advance for any help. Cheers!!
Firstly, I would suggest that you store your data in namedtuple
s or a custom class, instead of just raw tuples. This way you can access the attributes by name (e.g. by using operator.attrgetter
). The other option is to use a dict
.
Then, you can simply do the following (assuming your items are dict
s):
[item for item in q_list if all((item[attr] == q_dict[attr])
for q_dict in filters)]
This of course only works if your conditions always follow the pattern shown in your question (no ||
, no !=
or stuff like that).
If you can only use tuples, then you can create a dictionary that maps the names of attributes to tuples-indices.
Now, when you print them, you can just create a list of attributes that should be printed, and use it like this:
for item in filtered_items:
print "\t".join(item[attr] for attr in attributes_to_print) + "\n"
The most common approach is to use a list comprehension, for instance to filter by username:
[x for x in q_list if x[2] == 'John Dee']
You can also use filter
:
filter(lambda x: x[2] == 'John Dee', q_list)
You can thus use your q_dict
to filter by user and/or queue easily.
[x for x in q_list if [x[2].lower().replace(' ', ''), x[7]] == q_dict.values()]
As suggested in Space_C0wb0y's answer, using namedtuple
would be a good approach, you can map out the fields rather easily:
fields = ['field1', 'field2', 'username', 'field4', 'field5', 'field6', 'field7', 'queue', 'field9', 'field10']
Item = namedtuple('Item', fields)
For the sake of the argument, let's convert the tuples in your list to namedtuples:
q_namedtuple = [Item(*x) for x in q_list]
And then you can filter dynamically based on the query params. For instance to get a list of items where all query params match the given fields:
[item for item in q_namedtuple if all(getattr(item, k) == v for k, v in q_dict.iteritems())]
That is assuming the username in q_dict
actually matched the field for username...it doesn't in your example but you can work around that. Similarly you could create a list of dicts, that'd make working with your list a bit easier:
q_list_of_dicts = [dict(zip(fields, x)) for x in q_list]
Which would give you a list of dicts like:
{'field1': '102.vmtest2',
...etc
'queue': 'short',
'username': 'John Dee'}
Then you can filter similarly:
[item for item in q_list_of_dicts if all(item.get(k) == v for k, v in q_dict.iteritems())]
Personally I think I prefer the dict approach. Whole things rolled up using for loops instead (and one generator expression):
results = []
for item in q_list:
d = dict(zip(fields, item))
# use some other logic to filter
if all(d.get(k) == v for k, v in q_dict.iteritems()):
results.append(d)
For the formatDate, it may be more complex, but for simple equality, you can use eval:
test_string = "username == 'John Dee' && queue == 'short'"
for a, b, username, formatDate, d, e, f, queue, g, h in q_list:
if eval(test_string.replace('&&', 'and')):
print '%s: %s' % (username, queue)
you can make 'or' statements by replacing '||' and this is fairly dynamic
don't forget to put the values in quotes (not queue == short
but queue == 'short'
)
the result on the given q_list is:
John Dee: short
John Dee: short
once you have your user input parsed for the filter conditions, check each entry for each condition, saving a set of indices into q_list which match each individual condition, then do a set intersection to find the indices which match all of the conditions
in pseudo-ish code:
n = len(q_list)
accepted = set(range(n))
for condition in conditions:
accepted = accepted.intersection({i for i in range(n) if condition_holds(q_list(i), condition)})
result = [q_list[i] for i in accepted]
精彩评论