Cách hiệu quả để loại bỏ các phím có chuỗi trống khỏi một câu chính tả


116

Tôi có một mệnh lệnh và muốn xóa tất cả các khóa có các chuỗi giá trị trống.

metadata = {u'Composite:PreviewImage': u'(Binary data 101973 bytes)',
            u'EXIF:CFAPattern2': u''}

Cách tốt nhất để làm việc này là gì?

Câu trả lời:


194

Python 2.X

dict((k, v) for k, v in metadata.iteritems() if v)

Python 2.7 - 3.X

{k: v for k, v in metadata.items() if v is not None}

Lưu ý rằng tất cả các khóa của bạn đều có giá trị. Chỉ là một số giá trị trong số đó là chuỗi trống. Không có cái gọi là khóa trong một mệnh đề mà không có giá trị; nếu nó không có giá trị, nó sẽ không ở trong dict.


29
+1. Điều quan trọng cần lưu ý là điều này không thực sự xóa các khóa khỏi từ điển hiện có. Đúng hơn, nó tạo ra một từ điển mới. Thông thường đây chính xác là những gì ai đó muốn và có lẽ là những gì OP cần, nhưng nó không phải là những gì OP yêu cầu.
Steven Rumbalski

18
Điều này cũng giết chết v = 0, điều này tốt, nếu đó là những gì được mong muốn.
Paul

2
Điều này cũng giải thích v = False, không chính xác như những gì OP yêu cầu.
Amir

4
@shredding: Ý bạn là .items().
BrenBarn

6
Đối với các phiên bản sau của python bạn cũng nên sử dụng máy phát điện từ điển:{k: v for k, v in metadata.items() if v is not None}
Schiavini

75

Nó có thể ngắn hơn giải pháp của BrenBarn (và tôi nghĩ dễ đọc hơn)

{k: v for k, v in metadata.items() if v}

Đã thử nghiệm với Python 2.7.3.


13
Điều này cũng giết chết các giá trị bằng không.
Paul

10
Để bảo toàn 0 (không), bạn có thể sử dụng ... if v!=Nonenhư vậy: {k: v for k, v in metadata.items() if v!=None}
Dannid

1
{k: v for k, v trong metadata.items () if v! = None} không loại bỏ các chuỗi trống.
philgo20

1
khả năng hiểu từ điển chỉ được hỗ trợ với Python 2.7+ để tương thích với các phiên bản trước, vui lòng sử dụng giải pháp của @ BrenBarn.
Pavan Gupta

12
Nên luôn so sánh Không có với, 'không phải', thay vì '! ='. stackoverflow.com/a/14247419/2368836
rocktheartsm4l

21

Nếu bạn thực sự cần sửa đổi từ điển gốc:

empty_keys = [k for k,v in metadata.iteritems() if not v]
for k in empty_keys:
    del metadata[k]

Lưu ý rằng chúng tôi phải tạo danh sách các khóa trống vì chúng tôi không thể sửa đổi từ điển trong khi lặp qua nó (như bạn có thể nhận thấy). Tuy nhiên, điều này ít tốn kém hơn (khôn ngoan về bộ nhớ) so với việc tạo một từ điển hoàn toàn mới, trừ khi có rất nhiều mục nhập có giá trị trống.


điều này cũng sẽ xóa giá trị 0 và 0 không trống
JVK

2
Nếu bạn đang sử dụng Python 3+, bạn phải thay thế .iteritems()bằng .items(), đầu tiên không hoạt động nữa trong các phiên bản Python mới nhất.
Mariano Ruiz

12

Giải pháp của BrenBarn là lý tưởng (và pythonic, tôi có thể thêm vào). Tuy nhiên, đây là một giải pháp (fp) khác:

from operator import itemgetter
dict(filter(itemgetter(1), metadata.items()))

12

Nếu bạn muốn có một cách tiếp cận đầy đủ tính năng nhưng ngắn gọn để xử lý các cấu trúc dữ liệu trong thế giới thực thường được lồng vào nhau và thậm chí có thể chứa các chu trình, tôi khuyên bạn nên xem tiện ích remap từ gói tiện ích boltons .

Sau khi pip install boltonshoặc sao chép iterutils.py vào dự án của bạn, chỉ cần thực hiện:

from boltons.iterutils import remap

drop_falsey = lambda path, key, value: bool(value)
clean = remap(metadata, visit=drop_falsey)

Trang này có nhiều ví dụ khác, bao gồm những ví dụ làm việc với các đối tượng lớn hơn nhiều từ API của Github.

Đó là Python thuần túy, vì vậy nó hoạt động ở mọi nơi và được thử nghiệm hoàn toàn bằng Python 2.7 và 3.3+. Hơn hết, tôi đã viết nó cho chính xác những trường hợp như thế này, vì vậy nếu bạn tìm thấy một trường hợp nó không xử lý được, bạn có thể sửa lỗi cho tôi ngay tại đây .


1
Giải pháp này hoạt động tốt cho một vấn đề tương tự mà tôi gặp phải: loại bỏ các giá trị trống khỏi danh sách lồng nhau sâu bên trong từ điển. Cảm ơn!
Nicholas Tulach,

1
Điều này là tốt, vì bạn không phải phát minh lại bánh xe và cung cấp giải pháp cho các đối tượng lồng nhau. Cảm ơn!
vekerdyb

1
Tôi thực sự thích bài viết bạn đã viết cho thư viện của mình, và đây là một thư viện hữu ích!
lifeelogger 13/1018

11

Dựa trên giải pháp của Ryan , nếu bạn cũng có danh sách và từ điển lồng nhau:

Đối với Python 2:

def remove_empty_from_dict(d):
    if type(d) is dict:
        return dict((k, remove_empty_from_dict(v)) for k, v in d.iteritems() if v and remove_empty_from_dict(v))
    elif type(d) is list:
        return [remove_empty_from_dict(v) for v in d if v and remove_empty_from_dict(v)]
    else:
        return d

Đối với Python 3:

def remove_empty_from_dict(d):
    if type(d) is dict:
        return dict((k, remove_empty_from_dict(v)) for k, v in d.items() if v and remove_empty_from_dict(v))
    elif type(d) is list:
        return [remove_empty_from_dict(v) for v in d if v and remove_empty_from_dict(v)]
    else:
        return d

1
Ha, phần mở rộng tốt đẹp! Nó là một giải pháp tốt cho các từ điển như sau:d = { "things": [{ "name": "" }] }
Ryan Shea

6

Nếu bạn có một từ điển lồng nhau và bạn muốn điều này hoạt động ngay cả đối với các phần tử con trống, bạn có thể sử dụng một biến thể đệ quy của đề xuất của BrenBarn:

def scrub_dict(d):
    if type(d) is dict:
        return dict((k, scrub_dict(v)) for k, v in d.iteritems() if v and scrub_dict(v))
    else:
        return d

Sử dụng items()thay vì iteritems()cho Python 3
andydavies

6

Trả lời nhanh (TL; DR)

Ví dụ01

### example01 -------------------

mydict  =   { "alpha":0,
              "bravo":"0",
              "charlie":"three",
              "delta":[],
              "echo":False,
              "foxy":"False",
              "golf":"",
              "hotel":"   ",                        
            }
newdict =   dict([(vkey, vdata) for vkey, vdata in mydict.iteritems() if(vdata) ])
print newdict

### result01 -------------------
result01 ='''
{'foxy': 'False', 'charlie': 'three', 'bravo': '0'}
'''

Câu trả lời chi tiết

Vấn đề

  • Bối cảnh: Python 2.x
  • Tình huống: Nhà phát triển muốn sửa đổi từ điển để loại trừ các giá trị trống
    • aka xóa các giá trị trống khỏi từ điển
    • hay còn gọi là xóa các khóa có giá trị trống
    • aka lọc từ điển cho các giá trị không trống trên mỗi cặp khóa-giá trị

Giải pháp

  • example01 sử dụng cú pháp đọc hiểu danh sách python với điều kiện đơn giản để loại bỏ các giá trị "trống"

Cạm bẫy

  • example01 chỉ hoạt động trên bản sao của từ điển gốc (không sửa đổi tại chỗ)
  • example01 có thể tạo ra kết quả không mong đợi tùy thuộc vào ý nghĩa của nhà phát triển là "trống"
    • Nhà phát triển có ý định giữ các giá trị sai lệch không?
    • Nếu các giá trị trong từ điển không phải là chuỗi ký tự, nhà phát triển có thể bị mất dữ liệu không mong muốn.
    • result01 cho thấy chỉ có ba cặp khóa-giá trị được giữ nguyên từ tập hợp ban đầu

Ví dụ thay thế

  • example02 giúp đối phó với những cạm bẫy tiềm ẩn
  • Cách tiếp cận là sử dụng một định nghĩa chính xác hơn về "trống" bằng cách thay đổi điều kiện.
  • Ở đây chúng tôi chỉ muốn lọc ra các giá trị đánh giá thành chuỗi trống.
  • Ở đây chúng tôi cũng sử dụng .strip () để lọc ra các giá trị chỉ chứa khoảng trắng.

Ví dụ02

### example02 -------------------

mydict  =   { "alpha":0,
              "bravo":"0",
              "charlie":"three",
              "delta":[],
              "echo":False,
              "foxy":"False",
              "golf":"",
              "hotel":"   ",
            }
newdict =   dict([(vkey, vdata) for vkey, vdata in mydict.iteritems() if(str(vdata).strip()) ])
print newdict

### result02 -------------------
result02 ='''
{'alpha': 0,
  'bravo': '0', 
  'charlie': 'three', 
  'delta': [],
  'echo': False,
  'foxy': 'False'
  }
'''

Xem thêm



4

Dựa trên các câu trả lời từ Patriciasznneonneo , đồng thời tính đến khả năng bạn có thể muốn xóa các khóa chỉ có một số thứ giả mạo nhất định (ví dụ '') chứ không phải những thứ khác (ví dụ 0), hoặc có thể bạn thậm chí muốn bao gồm một số thứ trung thực (ví dụ 'SPAM') , sau đó bạn có thể tạo một danh sách hit cụ thể:

unwanted = ['', u'', None, False, [], 'SPAM']

Thật không may, điều này không hoàn toàn hoạt động, vì ví dụ như 0 in unwantedđánh giá True. Chúng ta cần phân biệt giữa 0những thứ giả dối và những thứ giả tạo khác, vì vậy chúng ta phải sử dụng is:

any([0 is i for i in unwanted])

... đánh giá đến False.

Bây giờ hãy sử dụng nó vào delnhững việc không mong muốn:

unwanted_keys = [k for k, v in metadata.items() if any([v is i for i in unwanted])]
for k in unwanted_keys: del metadata[k]

Nếu bạn muốn có một từ điển mới, thay vì sửa đổi metadatatại chỗ:

newdict = {k: v for k, v in metadata.items() if not any([v is i for i in unwanted])}

thật sự tốt đẹp shot, nó giải quyết nhiều vấn đề cùng một lúc và nó giải quyết các câu hỏi, cảm ơn bạn để làm cho nó rõ ràng
jlandercy

Mát mẻ! Nó hoạt động cho ví dụ này. Tuy nhiên, nó không hoạt động khi một mục trong từ điển là[]
jsga

2

Tôi đã đọc tất cả các câu trả lời trong chủ đề này và một số cũng được đề cập đến chủ đề này: Loại bỏ các ô trống trong từ điển lồng nhau bằng hàm đệ quy

Tôi ban đầu đã sử dụng giải pháp ở đây và nó hoạt động tuyệt vời:

Nỗ lực 1: Quá nóng (không hiệu quả hoặc sẽ có trong tương lai) :

def scrub_dict(d):
    if type(d) is dict:
        return dict((k, scrub_dict(v)) for k, v in d.iteritems() if v and scrub_dict(v))
    else:
        return d

Nhưng một số lo ngại về hiệu suất và khả năng tương thích đã được nêu ra trong thế giới Python 2.7:

  1. sử dụng isinstancethay vìtype
  2. bỏ cuộn danh sách comp vào forvòng lặp để có hiệu quả
  3. sử dụng python3 an toàn itemsthay vìiteritems

Nỗ lực 2: Quá lạnh (Thiếu ghi nhớ) :

def scrub_dict(d):
    new_dict = {}
    for k, v in d.items():
        if isinstance(v,dict):
            v = scrub_dict(v)
        if not v in (u'', None, {}):
            new_dict[k] = v
    return new_dict

DOH! Đây không phải là đệ quy và hoàn toàn không phải là công cụ ghi nhớ.

Nỗ lực 3: Vừa phải (cho đến nay) :

def scrub_dict(d):
    new_dict = {}
    for k, v in d.items():
        if isinstance(v,dict):
            v = scrub_dict(v)
        if not v in (u'', None, {}):
            new_dict[k] = v
    return new_dict

1
trừ khi tôi bị mù, tôi thấy rằng nỗ lực 2 và 3 hoàn toàn giống nhau ...
luckyguy73

1

Các môn phái kết hợp với Mảng

  • Câu trả lời ở Attempt 3: Just Right (cho đến nay) từ câu trả lời của BlissRage không xử lý đúng các phần tử của mảng. Tôi bao gồm một bản vá trong trường hợp bất kỳ ai cần nó. Phương thức là xử lý danh sách với khối câu lệnh if isinstance(v, list):, sẽ xóa danh sách bằng cách sử dụng scrub_dict(d)triển khai ban đầu .
    @staticmethod
    def scrub_dict(d):
        new_dict = {}
        for k, v in d.items():
            if isinstance(v, dict):
                v = scrub_dict(v)
            if isinstance(v, list):
                v = scrub_list(v)
            if not v in (u'', None, {}):
                new_dict[k] = v
        return new_dict

    @staticmethod
    def scrub_list(d):
        scrubbed_list = []
        for i in d:
            if isinstance(i, dict):
                i = scrub_dict(i)
            scrubbed_list.append(i)
        return scrubbed_list

tuyệt vời . . . Tôi đã thực hiện thay đổi này trong cơ sở mã nhưng đã bỏ lỡ nhận xét của bạn _ / _
BlissRage

0

Một cách thay thế bạn có thể làm điều này, là sử dụng từ điển hiểu. Điều này phải tương thích với2.7+

result = {
    key: value for key, value in
    {"foo": "bar", "lorem": None}.items()
    if value
}

0

Đây là một tùy chọn nếu bạn đang sử dụng pandas:

import pandas as pd

d = dict.fromkeys(['a', 'b', 'c', 'd'])
d['b'] = 'not null'
d['c'] = ''  # empty string

print(d)

# convert `dict` to `Series` and replace any blank strings with `None`;
# use the `.dropna()` method and
# then convert back to a `dict`
d_ = pd.Series(d).replace('', None).dropna().to_dict()

print(d_)

0

Một số phương thức được đề cập ở trên bỏ qua nếu có bất kỳ số nguyên nào và thả nổi với các giá trị 0 & 0.0

Nếu ai đó muốn tránh những điều trên có thể sử dụng mã bên dưới (loại bỏ các chuỗi trống và giá trị Không có từ từ điển lồng nhau và danh sách lồng nhau):

def remove_empty_from_dict(d):
    if type(d) is dict:
        _temp = {}
        for k,v in d.items():
            if v == None or v == "":
                pass
            elif type(v) is int or type(v) is float:
                _temp[k] = remove_empty_from_dict(v)
            elif (v or remove_empty_from_dict(v)):
                _temp[k] = remove_empty_from_dict(v)
        return _temp
    elif type(d) is list:
        return [remove_empty_from_dict(v) for v in d if( (str(v).strip() or str(remove_empty_from_dict(v)).strip()) and (v != None or remove_empty_from_dict(v) != None))]
    else:
        return d

0

"Vì tôi hiện cũng đang viết một ứng dụng dành cho máy tính để bàn cho công việc của mình với Python, tôi thấy trong ứng dụng nhập dữ liệu có rất nhiều mục nhập và một số mục không bắt buộc nên người dùng có thể để trống, vì mục đích xác thực, rất dễ lấy tất cả các mục nhập và sau đó loại bỏ khóa hoặc giá trị trống của từ điển. Vì vậy, đoạn mã của tôi ở trên cho thấy cách chúng tôi có thể dễ dàng lấy chúng ra, sử dụng tính năng hiểu từ điển và giữ phần tử giá trị từ điển không trống. Tôi sử dụng Python 3.8.3

data = {'':'', '20':'', '50':'', '100':'1.1', '200':'1.2'}

dic = {key:value for key,value in data.items() if value != ''}

print(dic)

{'100': '1.1', '200': '1.2'}

Xin vui lòng đề cập đến phiên bản python nó cũng sẽ hỗ trợ phiên bản mới nhất?
HaseeB Mir

Câu trả lời của bạn hiện được gắn cờ vì chất lượng thấp có thể bị xóa. Hãy đảm bảo rằng câu trả lời của bạn có chứa lời giải thích ngoài bất kỳ mã nào.
Tim Stack

@TimStack Vui lòng đề xuất xóa đối với các câu trả lời LQ.
10 Rep

@ 10Rep Tôi sẽ không khuyên bạn xóa câu trả lời có thể hoạt động như một giải pháp nhưng chỉ đơn thuần là thiếu bất kỳ nhận xét mô tả nào. Tôi muốn thông báo cho người dùng và dạy họ câu trả lời tốt hơn trông như thế nào.
Tim Stack

@HasseB Mir Tôi sử dụng Python 3.8.3 mới nhất
KokoEfraim

-2

Một số điểm chuẩn:

1. Đọc hiểu danh sách tạo lại dict

In [7]: %%timeit dic = {str(i):i for i in xrange(10)}; dic['10'] = None; dic['5'] = None
   ...: dic = {k: v for k, v in dic.items() if v is not None} 
   1000000 loops, best of 7: 375 ns per loop

2. Danh sách hiểu được tạo lại dict bằng cách sử dụng dict ()

In [8]: %%timeit dic = {str(i):i for i in xrange(10)}; dic['10'] = None; dic['5'] = None
   ...: dic = dict((k, v) for k, v in dic.items() if v is not None)
1000000 loops, best of 7: 681 ns per loop

3. Khóa lặp và xóa nếu v là Không có

In [10]: %%timeit dic = {str(i):i for i in xrange(10)}; dic['10'] = None; dic['5'] = None
    ...: for k, v in dic.items():
    ...:   if v is None:
    ...:     del dic[k]
    ...: 
10000000 loops, best of 7: 160 ns per loop

vì vậy vòng lặp và xóa nhanh nhất là 160ns, đọc danh sách chậm hơn một nửa ở ~ 375ns và với cuộc gọi đến dict()chậm hơn một nửa ~ 680ns.

Việc kết hợp 3 thành một hàm sẽ đưa nó trở lại khoảng 275ns. Ngoài ra, đối với tôi, PyPy nhanh gấp đôi so với trăn neet.


Vòng lặp và xóa cũng có thể tạo ra một RunTimeError, vì nó không hợp lệ để sửa đổi từ điển trong khi lặp lại một dạng xem. docs.python.org/3/library/stdtypes.html s4.10.1
Airsource TNHH

ah man vâng ok trong python 3 điều đó đúng nhưng không phải trong python 2.7 vì các mục trả về một danh sách, vì vậy bạn phải gọi list(dic.items())trong py 3. Đọc hiểu chính tả ftw sau đó? del vẫn có vẻ nhanh hơn đối với tỷ lệ giá trị Null / rỗng thấp. Tôi đoán việc xây dựng danh sách đó cũng không tốt cho việc tiêu thụ bộ nhớ hơn là chỉ tạo lại các câu lệnh.
Richard Mathie,
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.