Hàm nối chuổi theo điều kiện (6 người xem)

Liên hệ QC

Người dùng đang xem chủ đề này

Miền Cát Trắng

Thành viên hoạt động
Tham gia
18/5/13
Bài viết
171
Được thích
37
Xin chào các bạn,đầu xuân chúc các bạn luôn mạnh khỏe,luôn hạnh phúc và đạt nhiều thành công trong cuộc sống.
Tôi có một bài toán đã đề cập trong file kèm, các bạn xem và giúp mình với.
Cảm ơn!
 

File đính kèm

Hix!@ThầyNDU và Anh Nghĩa!
Em đã test cả 2 bài mới nhất và kết quả thật sự là đẹp mỹ mãn lắm rồi ạ...
Xin cảm ơn Thầy và Anh rất nhiều và nhân dịp đầu năm chúc Thầy và Anh luôn vui vẻ và mãi gắn bó với GPE để bầu trời tươi đẹp của thế hệ trẻ sau này được rộng mở.
 
Upvote 0
Hix!@ThầyNDU và Anh Nghĩa!
Em đã test cả 2 bài mới nhất và kết quả thật sự là đẹp mỹ mãn lắm rồi ạ...
Xin cảm ơn Thầy và Anh rất nhiều và nhân dịp đầu năm chúc Thầy và Anh luôn vui vẻ và mãi gắn bó với GPE để bầu trời tươi đẹp của thế hệ trẻ sau này được rộng mở.
Bài đó chưa có hay đâu, bài này mới đã nè!

[GPECODE=vb]
Function TextJoin(ByVal ConditionalColumn As Range, _
ByVal ConditionalCell As Range, _
ByVal DataColumn As Range) As String
''On Error Resume Next
If ConditionalCell.Value = "" Then Exit Function
Dim r As Long
Dim Dict As Object
Dim Cond, CondCell, CondColArr, DataColArr, Tmp
CondCell = ConditionalCell.Value
If IsArray(ConditionalColumn) Then
CondColArr = ConditionalColumn
DataColArr = DataColumn
Else
ReDim CondColArr(1 To 1, 1 To 1)
ReDim DataColArr(1 To 1, 1 To 1)
CondColArr(1, 1) = ConditionalColumn
DataColArr(1, 1) = DataColumn
End If
Set Dict = CreateObject("Scripting.Dictionary")
If TypeName(CondCell) = "String" Then
For r = 1 To UBound(CondColArr)
If CStr(CondColArr(r, 1)) = CStr(CondCell) Then
Tmp = DataColArr(r, 1)
If Not Dict.Exists(Tmp) And Tmp > "" Then
Dict.Add Tmp, ""
End If
End If
Next
Else
For r = 1 To UBound(CondColArr)
Cond = CondColArr(r, 1)
If TypeName(Cond) <> "String" Then
If CDbl(Cond) = CDbl(CondCell) Then
Tmp = DataColArr(r, 1)
If Not Dict.Exists(Tmp) And Tmp > "" Then
Dict.Add Tmp, ""
End If
End If
End If
Next
End If
If Dict.Count Then
TextJoin = Join(Dict.Keys, ", ")
End If
Dict.RemoveAll
Set Dict = Nothing
End Function[/GPECODE]

File mới của bạn, nếu dùng hàm JoinIf của tôi thì sẽ thế này:
Mã:
=JoinIf(", ",$H$8:$H$100,[COLOR=#ff0000]"="&K8[/COLOR],$I$8:$I$100)
---------------------

Ai lại làm thế! Hàm phải tổng quát chứ
Trường hợp này nó giống với Advanced Filter và Filter đấy (dạng đặc biệt) và ta cũng phải lường trước trong code

Em đã làm một cách tổng quát cho Hàm TextJoin và khi so sánh thì Hàm JoinIf còn nhiều trường hợp chưa chính xác.

Xem file.
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Bổ sung vấn đề so sánh chữ HOA, chữ Thường

[GPECODE=vb]
Function TextJoin(ByVal ConditionalColumn As Range, _
ByVal ConditionalCell As Range, _
ByVal DataColumn As Range, _
Optional ByVal Compare As Boolean) As String
''Voi Compare neu phan biet chu HOA chu Thuong
''thi chon la TRUE, khong thi thoi.

''On Error Resume Next
Application.Volatile
If ConditionalCell.Value = "" Then Exit Function
Dim r As Long
Dim Dict As Object
Dim Cond, CondCell, CondColArr, DataColArr, Tmp
CondCell = ConditionalCell.Value
If IsArray(ConditionalColumn) Then
CondColArr = ConditionalColumn
DataColArr = DataColumn
Else
ReDim CondColArr(1 To 1, 1 To 1)
ReDim DataColArr(1 To 1, 1 To 1)
CondColArr(1, 1) = ConditionalColumn
DataColArr(1, 1) = DataColumn
End If
Set Dict = CreateObject("Scripting.Dictionary")
If TypeName(CondCell) = "String" Then
CondCell = CStr(CondCell)
If Compare Then
For r = 1 To UBound(CondColArr)
If CStr(CondColArr(r, 1)) = CondCell Then
Tmp = DataColArr(r, 1)
If Not Dict.Exists(Tmp) And Tmp > "" Then
Dict.Add Tmp, ""
End If
End If
Next
Else
CondCell = UCase(CondCell)
For r = 1 To UBound(CondColArr)
If CStr(UCase(CondColArr(r, 1))) = CondCell Then
Tmp = DataColArr(r, 1)
If Not Dict.Exists(Tmp) And Tmp > "" Then
Dict.Add Tmp, ""
End If
End If
Next
End If
Else
For r = 1 To UBound(CondColArr)
Cond = CondColArr(r, 1)
If TypeName(Cond) <> "String" Then
If CDbl(Cond) = CDbl(CondCell) Then
Tmp = DataColArr(r, 1)
If Not Dict.Exists(Tmp) And Tmp > "" Then
Dict.Add Tmp, ""
End If
End If
End If
Next
End If
If Dict.Count Then
TextJoin = Join(Dict.Keys, ", ")
End If
Dict.RemoveAll
Set Dict = Nothing
End Function[/GPECODE]

Nếu bạn không cần so sánh chữ HOA, chữ thường thì làm công thức bình thường:

=TextJoin($B$2:$B$94,E2,$C$2:$C$94)

Nhưng nếu bạn phân biệt chỉ tính chữ HOA hoặc chữ thường thì bạn thêm TRUE:

=TextJoin($B$2:$B$94,E2,$C$2:$C$94,TRUE)

Rất tiện lợi phải không!

Để ý rằng có 3 vòng lặp FOR ... NEXT trong hàm phải không các bạn, không phải ngại chạy chậm đâu nhé, thực tế, chúng chỉ chạy có 1 vòng lặp khi thỏa một điều kiện nhất định mà thôi.
 
Upvote 0
Em đã làm một cách tổng quát cho Hàm TextJoin và khi so sánh thì Hàm JoinIf còn nhiều trường hợp chưa chính xác.

Xem file.

Không chính xác là vì chú mày không biết xài hàm thôi
Hàm tôi so sánh dựa trên toán tử Like và Evaluate. Vậy, đối với dữ liệu dạng Number, nếu muốn so sánh CHÍNH XÁC thì phải thêm dấu "=" vào điều kiện
Ví dụ: Thay vì viết:
Mã:
=JoinIf(", ",$B$2:$B$94,E2,$C$2:$C$94)
Thì phải viết:
Mã:
=JoinIf(", ",$B$2:$B$94,[COLOR=#ff0000]"="&E2[/COLOR],$C$2:$C$94)
Hàm này hoạt động gần giống với SUMIF: Cho phép so sánh "=", ">", "<" vân... vân...
Ngoài ra, hàm hoạt động theo Array nên dữ liệu trên bảng tính là ngang hay dọc đều dùng được cả
---------------------
Còn cái chuyện Nghĩa cố tình đưa ra những dữ liệu tào lao để cố chứng minh rằng hàm tôi không chính xác thì tôi.. không quan tâm. Vì dữ liệu thật chẳng bao giờ như vậy cả
Nên nhớ rằng: Tôi viết hàm là để phục vụ cho nhu cầu có thật, dựa trên CSDL thật. Thành ra, ai thích cứ xài, không thích cũng không sao
Ẹc... Ẹc...
 
Upvote 0
Còn cái chuyện Nghĩa cố tình đưa ra những dữ liệu tào lao để cố chứng minh rằng hàm tôi không chính xác thì tôi.. không quan tâm. Vì dữ liệu thật chẳng bao giờ như vậy cả

Ẹc... Ẹc...
Không hẳn là cố tình đưa ra dữ liệu tào lao, mà vì:

Một, có thể một lúc nào đó người ta định dạng kiểu d/m/y nhưng cũng có thể người ta định dạng kiểu khác thì hàm của Thầy chưa đáp ứng được.

Hai, cũng tương tự hàm SUMIF, cột điều kiện là rỗng thì nó không tính

Ba, dùng hàm Evaluate để tính toán, với dữ liệu lớn chắc chắn 100% sẽ chậm hơn phép so sánh bằng.

-------------------------------------------

Cũng như Thầy nói, ai dùng hàm nào cũng được, miễn sao đáp ứng được hiệu quả nhu cầu của mình sử dụng.

Riêng việc đổi chiều dọc thành chiều ngang thì thêm vài phép tính nữa thôi mà Thầy. Quan điểm của em có thể viết thêm sửa đổi sao cho khi sử dụng người dùng cảm thấy đơn giản dễ hiểu, hiệu quả, nhanh chóng, chính xác, còn hàm có viết tràn lan đại hải cũng không sao (đâu ai quan tâm đến hàm SUM của Excel nó được viết thế nào, họ chỉ biết dùng mà thôi).
 
Upvote 0
Một, có thể một lúc nào đó người ta định dạng kiểu d/m/y nhưng cũng có thể người ta định dạng kiểu khác thì hàm của Thầy chưa đáp ứng được.
Cái này như tôi đã nói ở trên: Thêm dấu "=" để so sánh rồi còn gì
Hai, cũng tương tự hàm SUMIF, cột điều kiện là rỗng thì nó không tính
Quá dễ, nếu muốn sửa code
Ba, dùng hàm Evaluate để tính toán, với dữ liệu lớn chắc chắn 100% sẽ chậm hơn phép so sánh bằng.
Cái này thì Nghĩa đã suy nghĩ quá xa rồi. Nối chuổi có bao giờ ra kết quả mấy ngàn từ không? Cho dù có ra được kết quả cũng chẳng đọc được

-------------------------------------------
Cũng như Thầy nói, ai dùng hàm nào cũng được, miễn sao đáp ứng được hiệu quả nhu cầu của mình sử dụng.

Riêng việc đổi chiều dọc thành chiều ngang thì thêm vài phép tính nữa thôi mà Thầy. Quan điểm của em có thể viết thêm sửa đổi sao cho khi sử dụng người dùng cảm thấy đơn giản dễ hiểu, hiệu quả, nhanh chóng, chính xác, còn hàm có viết tràn lan đại hải cũng không sao (đâu ai quan tâm đến hàm SUM của Excel nó được viết thế nào, họ chỉ biết dùng mà thôi).
Như ta đã biết, đã gọi là SO SÁNH thì không chỉ có SO SÁNH BẰNG. Còn có LỚN HƠN, NHỎ HƠN, KHÁC hoặc GẦN GIỐNG nữa... và JoinIf của tôi viết ra là để phục vụ cho nhu cầu này (gần giống với SUMIF)
SUMIF là do cả tập đoàn MS viết ra mà trong một số trường hợp cụ thể còn có sai sót (đã bàn trên diễn đàn nhiều lần rồi). Vậy nên MS cũng viết thêm hàm SUMPRODUCT để ta tùy ý phát biểu điều kiện... Thực chất chúng ta không thể viết 1 hàm tổng quát đến mức có thể đáp ứng mọi dữ liệu và mọi nhu cầu... nên tôi cũng bắt chước MS, viết thêm hàm JoinText để người dùng có thêm lựa chọn trong trường hợp điều kiện so sánh phức tạp
Vậy:
- Nếu cảm thấy SUMIF không chính xác thì dùng SUMPRODUCT
- Cũng giống như nếu cảm thấy JoinIf không chính xác thì dùng JoinText
Thế thôi
 
Upvote 0
Cái này như tôi đã nói ở trên: Thêm dấu "=" để so sánh rồi còn gì

Một dữ liệu vừa là chuỗi, vừa là số, mà để dấu "=" thì đúng phần số và sai phần chuỗi, Thầy thử nghĩ coi, nếu phải mỗi ô mỗi công thức khác nhau thì sao được hả Thầy?

Còn việc so sánh lớn hơn, nhỏ hơn thì bắt buộc dữ liệu điều kiện phải là dạng số

Và việc so sánh na ná giống có lẽ phải dữ liệu bằng chuỗi thôi.

Dĩ nhiên trình độ em được học từ Thầy, nhưng đây là ý tưởng để Thầy hoàn thiện, bổ sung thêm cho nó hoàn chỉnh mà thôi, em không có ý gì đâu ạ.
 
Lần chỉnh sửa cuối:
Upvote 0
Một dữ liệu vừa là chuỗi, vừa là số, mà để dấu "=" thì đúng phần số và sai phần chuỗi, Thầy thử nghĩ coi, nếu phải mỗi ô mỗi công thức khác nhau thì sao được hả Thầy?
Thì dùng hàm JoinText
Và việc so sánh na ná giống có lẽ phải dữ liệu bằng chuỗi thôi.
Na ná nghĩa là *Chuổi* ấy, đương nhiên là so sánh chuổi rồi. Code tôi dùng toán tử Like nên chẳng cần phải suy nghĩ gì nhiều, tự nhiên nó vẫn đạt được như vậy thôi (áp dụng giải thuật trong hàm Filter2DArray)
Dĩ nhiên trình độ em được học từ Thầy, nhưng đây là ý tưởng để Thầy hoàn thiện, bổ sung thêm cho nó hoàn chỉnh mà thôi, em không có ý gì đâu ạ.

Chú mày nói quá! Chỉ là nghiên cứu thôi (với lại tôi cũng không nghĩ phải hoàn thiện thêm gì nữa cả: 2 hàm JoinIf và JoinText là quá đủ để áp dụng)
------------------------------------------
(Đã dời bài đúng box)
 
Lần chỉnh sửa cuối:
Upvote 0
Web KT

Bài viết mới nhất

Back
Top Bottom