2009-03-17 29 views
7

Tôi đã cố gắng viết một phương thức mở rộng để bắt chước List.RemoveAll (Predicate).Phương thức mở rộng Từ điển <TKey,TValue> .Xóa tất cả? Có thể không?

Cho đến nay tôi đã nhận điều này:

public static void RemoveAll<TKey,TValue>(this Dictionary<TKey,TValue> dict, 
            Predicate<KeyValuePair<TKey,TValue>> condition) 
{ 
    Dictionary<TKey,TValue> temp = new Dictionary<TKey,TValue>(); 

    foreach (var item in dict) 
    { 
     if (!condition.Invoke(item)) 
      temp.Add(item.Key, item.Value); 
    } 

    dict = temp; 
} 

Bất kỳ con trỏ? Đây có phải là một triển khai hoàn toàn ngây thơ không?

+0

Bạn sẽ không muốn xóa các cặp từ điển bằng cách chỉ đối sánh vị từ của bạn bằng Khóa, thay vì KeyValuePair? – base2

Trả lời

16

Mã của bạn sẽ không hoạt động vì bạn đang chuyển lớp Từ điển theo giá trị. Điều này có nghĩa là nhiệm vụ cuối cùng (dict = temp) sẽ không hiển thị với chức năng gọi. Nó không phải là hợp pháp trong C# để vượt qua các mục tiêu phương pháp mở rộng bằng cách ref hoặc out (trong VB nó là hợp pháp để làm ByRef).

Thay vào đó, bạn sẽ cần sửa đổi nội tuyến Từ điển. Hãy thử như sau

public static void RemoveAll<TKey,TValue>(this Dictionary<TKey,TValue> dict, 
            Func<KeyValuePair<TKey,TValue>,bool> condition) 
{ 
    foreach (var cur in dict.Where(condition).ToList()) { 
     dict.Remove(cur.Key); 
    } 
} 

EDIT

hoán đổi thứ tự của Trường và ToList để giảm kích thước của bộ nhớ phân bổ danh sách. Nó bây giờ sẽ chỉ phân bổ một danh sách cho các mục cần xóa.

+0

Có bất lợi khi phân bổ đủ bộ nhớ cho danh sách khóa mọi lúc. nhưng chắc chắn đơn giản – ShuggyCoUk

+0

Không thực sự làm việc mặc dù ... –

+0

@Rob như thế nào? Hoạt động tốt cho dữ liệu mẫu Tôi đã sử dụng – JaredPar

4
public static void RemoveAll<TKey,TValue>(
    this Dictionary<TKey,TValue> dict, 
    Predicate<KeyValuePair<TKey,TValue>> condition) 
{ 
    var toRemove = new List<TKey>(); 

    foreach (var item in dict) 
    { 
     if (!condition(item)) 
      toRemove.Add(item); 
    } 
    foreach (var key in toRemove) 
    { 
     dict.Remove(key); 
    } 
} 

Nếu số lượng các phím để loại bỏ là tương đối nhỏ với kích thước từ điển này sẽ nhanh hơn (nếu số lượng loại bỏ có khả năng là không bạn có thể làm điều này thậm chí nhanh hơn bằng cách uể oải tạo danh sách toRemove là tốt.

Điều này sẽ giảm xuống giống như câu trả lời được cập nhật của Jared nhưng cho phép bạn trì hoãn việc tạo danh sách loại bỏ nếu bạn muốn. Nếu đây không phải là vấn đề (và bạn không có lý do gì để phá vỡ một phần thông qua quá trình sau đó Jared là sạch hơn và đơn giản hơn

+0

Không cần phải gọi điều kiện.Invoke (...) phương pháp trên điều kiện, bởi vì, nó đã là một đại biểu. Bạn chỉ có thể gọi điều kiện trực tiếp, ví dụ: điều kiện (mục). – base2

+0

@ base2 Tôi vừa sao chép kiểu gốc của người dùng. Tôi đồng ý là tốt hơn nếu không có Invoke, tôi sẽ thay đổi nó – ShuggyCoUk

1

Phương pháp đó sẽ không hoạt động vì thông số "dict" không được chuyển bởi refere nce, và trên thực tế không thể vì ref không được hỗ trợ như tham số đầu tiên của một phương thức mở rộng.

public static void RemoveAll<TKey,TValue>(this Dictionary<TKey,TValue> dict, 
           Predicate<KeyValuePair<TKey,TValue>> condition) 
{ 
    var temp = new List<TKey>(); 

    foreach (var item in dict) 
    { 
     if (!condition(item)) 
      temp.Add(item.Key); 
    } 

    foreach (var itemKey in temp) 
     dict.Remove(itemKey) 
} 

Tôi cũng muốn xem triển khai RemoveAllByKey và RemoveAllByValue.

0

Nhưng nếu bạn muốn, bạn có thể trả lại một từ điển mới và khác. Chữ ký của bạn sẽ thay đổi như thế này:

public static Dictionary<TKey, TValue> RemoveAll<TKey,TValue>(this Dictionary<TKey,TValue> dict, 
           Predicate<KeyValuePair<TKey,TValue>> condition) 

Và mã gọi sẽ nói:

var newDict = oldDict.RemoveAll(kvp=> kvp.Name.StartsWith("something")); 

Và, nếu bạn muốn sửa đổi oldDict, bạn sẽ gọi nó là như thế này:

oldDict = oldDict.RemoveAll(kvp=> kvp.Name.StartsWith("something")); 
Các vấn đề liên quan