2017-07-10 15 views
5

Tôi đang tìm kiếm một cách để lọc một mảng với một biểu thức đánh giá như:Làm thế nào để lọc một mảng có chức năng bảng tính?

Dim arr1(), arr2(), arr3() 

arr1 = Array(1, 2, 3, 4, 5)   ' > [1, 2, 3, 4, 5] 

arr2 = Map(arr1, "Values() * 2")  ' > [2, 4, 6, 8, 10] 

arr3 = Filter(arr2, "Values() > 6") ' > [8, 10] 

tôi đã thực hiện các chức năng Map với một UDF và với Application.Evaluate("INDEX(expression,)"), nhưng tôi đang đấu tranh để làm cho nó làm việc cho Filter:

Private arr_() 

Public Function Values() As Variant() 
    Values = arr_ 
End Function 

Public Function Map(arr(), expression As String) As Variant() 
    arr_ = arr 
    Map = Application.Evaluate("INDEX(" & expression & ",)") 
End Function 

Public Function Filter(arr(), expression As String) As Variant() 
    arr_ = arr 
    Filter = Application.Evaluate("INDEX(Values(), " & expression & ")") 
End Function 

Có cách nào khác hơn là lặp/dịch chuyển từng giá trị không? Maby với VLOOKUP?

+2

Tôi nghĩ rằng bạn sẽ cần phải sử dụng một vòng lặp. –

+0

Bạn đang sử dụng Excel nào? Có một cách nếu bạn đang sử dụng Office 365 Excel với TEXTJOIN. Nếu không, nó sẽ ngừng hoạt động. INDEX sẽ trả về một mảng có cùng kích thước với đầu vào, chúng sẽ được lấp đầy bằng 'FALSE' và các số có độ phân giải là TRUE. –

Trả lời

1

Mặc dù tôi là một fan hâm mộ lớn của mảng và ủy thác hầu hết công việc cho Excel được xây dựng trong, cho điều này tôi thấy rằng thích hợp nhất là làm công việc chính trong VBA, sử dụng Excel để Evaluate biểu thức cho các mục riêng lẻ.

Public Function FilterArr(arr(), expression As String) 
    Dim match As Boolean, i As Long, val 
    ReDim ret(LBound(arr) To UBound(arr)) 
    i = LBound(arr) - 1 
    On Error Resume Next 
    For Each val In arr 
    match = False 
    match = Application.Evaluate(val & expression) 
    If match Then 
     i = i + 1 
     ret(i) = val 
    End If 
    Next 
    If i >= LBound(arr) Then 
    ReDim Preserve ret(LBound(arr) To i) 
    FilterArr = ret 
    End If 
End Function 

Sub test() 
    Dim arr1(), arr2(), arr3() 
    arr1 = Array(10, 20, 30, 40, 50) 
    arr3 = FilterArr(arr1, ">25") ' <--- usage like this 
    ' arr3 = (30, 40, 50) 
End Sub 

tái bút: một tiện ích mở rộng thú vị sẽ là cho phép nhiều tiêu chí (ví dụ: AND chỉnh sửa cùng nhau) bằng cách sử dụng ParamArray. Tốt ứng cử viên cho công việc tương lai ...

+0

Như đã đề cập trong bài đăng, tôi đang tìm một hàm trang tính chứ không phải vòng lặp. Ý tưởng là gọi 'Application.Evaluate' chỉ một lần để duy trì hiệu suất tốt và cho phép mảng 2d/1d làm đầu vào. – michael

+0

@michael Có một lỗ hổng cơ bản trong những gì bạn đang nói. ** Làm cách nào để lọc một mảng 2D? ** Thông thường bạn chỉ định một cột và xóa hoặc ẩn toàn bộ các hàng. "Bộ lọc" của bạn kiểm tra tất cả các mục nhập, vì vậy phải làm gì khi một emtry không khớp, hãy xóa hàng? cột? Để xem lỗ hổng, 'Xóa 'một ô trong Excel và đọc kỹ câu hỏi mà nó nhắc bạn. –

+0

Dường như bạn đã trả lời câu hỏi của riêng mình: "Thông thường bạn chỉ định một cột và xóa hoặc ẩn toàn bộ các hàng". Bạn chỉ đơn giản là thiếu việc sử dụng mà các giá trị từ một phạm vi/cột đơn: 'Bộ lọc ([A1: A100] .Giá trị," Giá trị()> 100 ")'. – michael

1

Đầu tiên thay đổi các chức năng như sau ...

Public Function Filter(arr(), sValues As String, sCriteria As String) As Variant() 
    Dim Cnt As Long 
    arr_ = arr 
    Cnt = Application.Evaluate("SUMPRODUCT(--(" & sValues & sCriteria & "))") 
    If Cnt > 0 Then 
    Filter = Application.Evaluate("TRANSPOSE(INDEX(SMALL(IF(" & sValues & sCriteria & "," & _ 
     sValues & "),ROW(INDEX(A:A,1):INDEX(A:A," & Cnt & "))),0))") 
    Else 
    Filter = Array() 
    End If 
End Function 

Sau đó gọi nó là như thế này ...

+0

Cảm ơn, điều này làm việc với ví dụ của tôi nhưng 'SMALL' là cách quá đắt (hơn 20 giây cho 10k hàng) và nó không hoạt động với một chuỗi đánh giá (ví dụ: =" a "). – michael

+0

Vâng, tôi đã không thử nghiệm nó với nhiều hàng, nhưng tôi mong đợi nó sẽ khá đắt tiền. Và, trong khi nó có thể được sửa đổi để đối phó với các đánh giá chuỗi, tôi đoán không có sử dụng trong khi làm như vậy, vì nó sẽ không được thực tế. – Domenic

+0

Vâng, nó sẽ không thực tế. Tôi đã hy vọng sử dụng 'INDEX' với một hàng tăng dần dựa trên kết quả của việc đánh giá, nhưng nó dường như không thể với một hàm. Có vẻ như tôi sẽ phải tiếp tục sử dụng vòng lặp. – michael

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