2016-12-21 19 views
5

Tôi có 4 bộ từ điển sau đây trong một bộ sưu tập MongoDB gọi favoriteColors:iterating trên một từ điển để tạo ra một danh sách

{ "name" : "Johnny", "color" : "green" } 
{ "name" : "Steve", "color" : "blue" }, 
{ "name" : "Ben", "color" : "red" }, 
{ "name" : "Timmy", "color" : "cyan" } 

Tôi đang cố gắng để tạo ra một danh sách có thứ tự các giá trị màu phù hợp với một danh sách có thứ khác nhau. Ví dụ: nếu tôi có danh sách ["Johnny", "Steve", "Ben", "Johnny"], danh sách mới sẽ là ["green", "blue", "red", "green"].

Và nếu tôi có danh sách ["Steve", "Steve", "Ben", "Ben", "Johnny"] danh sách mới sẽ là ["blue", "blue", "red", "red", "green"].

Cách hay để thực hiện việc này bằng Python và/hoặc PyMongo. Đây là những gì tôi có cho đến nay nhưng nó không nhận ra bản sao.

name_list = ["Steve", "Steve", "Ben", "Ben", "Johnny"] 

color_list = [] 
for document in db.favoriteColors.aggregate([ 
    {"$match": {"name": {"$in": name_list }}}, 
    {"$project": {"color": 1}} 
]): 
    for k, v in document.iteritems(): 
     color_list.append(v) 

print color_list 
# ["blue", "red", "green"] 
+2

Bạn có thể tạo ra một bản đồ của ' 'name'' ->'' color'' và sau đó sử dụng bản đồ mà với 'name_list' để xây dựng các color_list? Tôi không biết đủ về mongodb để biết nếu điều này có thể được thực hiện hiệu quả, nhưng có vẻ như nó có thể là ... – mgilson

Trả lời

1

Trên thực tế, chúng ta có thể sử dụng khuôn khổ hợp với chế biến mặt hàng khác để làm hiệu quả này.

import pymongo 


client = pymongo.MongoClient() 
db = client.test # Or whatever is your database 
favoriteColors = db.favoriteColors 
first_list = ['Johnny', 'Steve', 'Ben', 'Johnny'] 

cursor = favoriteColors.aggregate([ 
    {'$match': {'name': {'$in': first_list}}}, 
    {'$project': {'part': {'$map': { 
     'input': first_list, 
     'as': 'inp', 
     'in': { 
      '$cond': [ 
       {'$eq': [ '$$inp', '$name']}, 
       '$color', 
       None 
      ] 
     } 
    }}}}, 
    {'$group': {'_id': None, 'data': {'$push': '$part'}}} 
]) 

Bởi vì chúng tôi $group bởi Không, con trỏ của chúng tôi chứa một tài liệu mà chúng tôi có thể lấy sử dụng next.Trong thực tế con đường chúng ta có thể xác minh rằng với print(list(cursor))

>>> import pprint 
>>> pprint.pprint(list(cursor)) 
[{'_id': None, 
    'data': [['green', None, None, 'green'], 
      [None, 'blue', None, None], 
      [None, None, 'red', None]]}] 

Từ đây, chúng ta cần phải giải nén những "dữ liệu" lĩnh vực trong tài liệu với zip, chuỗi đầu vào sử dụng chain.from_iterable và lọc ra các yếu tố đó là None .

from itertools import chain 

result = [item 
      for item in chain.from_iterable(zip(*next(cursor)['data'])) 
      if item is not None] 

nào trả về:

>>> result 
['green', 'blue', 'red', 'green'] 
+0

Bạn có thể chia sẻ điểm chuẩn của câu trả lời của mình với đủ truy vấn không? Nó có vẻ hiệu quả. Cảm ơn. –

+0

Điều này dường như hoạt động nhưng trông rất phức tạp. Phải có một cách đơn giản hơn để thực hiện việc này bằng cách sử dụng khung tổng hợp. Tại sao bạn cần tất cả các mục đó dưới '" $ project "' –

+0

@ jcmetz21 Tôi không nghĩ rằng điều này phức tạp, tôi sẽ thêm giải thích và có thể một cách khác để làm điều này nếu các phần tử trong danh sách đầu ra không cần xuất hiện theo thứ tự chính xác này nhưng nó sẽ không được * đơn giản hơn * – styvane

0

Nếu bộ dữ liệu nhỏ, bạn có thể hợp nhất các dấu gạch vào một lệnh mới.

Trong python3 bạn có thể làm một cái gì đó như thế này:

names = ["Steve", "Steve", "Ben", "Ben", "Johnny"] 
favorites = {d["name"]: d["color"] for d in db.favoriteColors.find()} 
colors = [favorites[name] for name in names] 
print(colors) 

Cập nhật

Như styvane nói tôi quên gọi phương thức find trên Collection. Trả lời được cập nhật tương ứng.

+0

Đối tượng thu thập không thể lặp lại được. Điều này sẽ thất bại thảm hại với một TypeError đẹp – styvane

+0

Đây là một cách làm sạch nếu các từ điển có trong python và có rất ít dữ liệu để lặp qua. –

+0

@ jcmetz21 Tôi không thấy được cách này là * một cách sạch sẽ *. 'db.favoriteColors' là một đối tượng' Collection' và không triển khai thực hiện giao thức vòng lặp để truy vấn này sẽ trả về một kiểu TypeError như tôi đã đề cập. – styvane

0

Bạn cũng có thể tạo một số mới dict từ các khoản tiền hiện tại của mình trong đó mọi giá trị dict["name"] sẽ được liên kết với giá trị dict["color"].

Ví dụ: dict mới sẽ như thế nào:

{"Jhonny": "green", "Steve": "blue"} 

Và bạn có thể sử dụng một chức năng giống như ví dụ dưới đây mà chấp nhận nhiều đối số và trả lại danh sách mong muốn (Ngoài ra, nó gắn None nếu có bất kỳ tên trong danh sách đầu vào mà không có mặt trong dicts mặc định):

Dưới đây là ví dụ của tôi:

a = { "name" : "Johnny", "color" : "green" } 
b = { "name" : "Steve", "color" : "blue" } 
c = { "name" : "Ben", "color" : "red" } 
d = { "name" : "Timmy", "color" : "cyan" } 

my_list = ["Steve", "Steve", "Ben", "Ben", "Johnny"] 

def iter_func(my_list = list, *args): 
    ne = {k["name"]:k["color"] for k in args} 
    return [ne[k] if k in ne.keys() else None for k in my_list] 

Output:

print(iter_func(my_list, a,b,c,d)) 
>>> ['blue', 'blue', 'red', 'red', 'green'] 

Ví dụ với None giá trị:

a = { "name" : "Johnny", "color" : "green" } 
b = { "name" : "Steve", "color" : "blue" } 
c = { "name" : "Ben", "color" : "red" } 
d = { "name" : "Timmy", "color" : "cyan" } 

my_list = ["Steve", "Steve", "Alex", "Ben", "Ben", "Johnny", "Mark"] 

def iter_func(my_list = list, *args): 
    ne = {k["name"]:k["color"] for k in args} 
    return [ne[k] if k in ne.keys() else None for k in my_list] 

Output:

print(iter_func(my_list, a,b,c,d)) 
>>> ['blue', 'blue', None, 'red', 'red', 'green', None] 
+1

Không phải cách để làm điều này. Nếu bạn có 1 triệu tài liệu phù hợp với truy vấn của mình thì sao? – styvane

+0

OP không nói rằng các truy vấn của anh ta rất lớn như bạn đã nói. Nhưng cảm ơn cho bình luận của bạn. Tôi sẽ cố gắng cải thiện câu trả lời của mình. –

Các vấn đề liên quan