Chuyển dữ liệu từ cột sang dòng bằng vba excel

Liên hệ QC
Tôi tuân thủ nội quy khi đăng bài

binhminh1408

Thành viên mới
Tham gia
9/9/24
Bài viết
2
Được thích
0
Chào các anh chị,
Em có một file muốn chuyển dữ liệu từ cột sang dòng. Trong file, em có mô tả dữ liệu nguồn và kết quả mong muốn. Anh chị hỗ trợ một đoạn code để ra kết quả ạ.
Em chân thành cảm ơn.
 

File đính kèm

  • chuyen cot sang dong.xlsx
    8.9 KB · Đọc: 15
Chào các anh chị,
Em có một file muốn chuyển dữ liệu từ cột sang dòng. Trong file, em có mô tả dữ liệu nguồn và kết quả mong muốn. Anh chị hỗ trợ một đoạn code để ra kết quả ạ.
Em chân thành cảm ơn.
Bạn thử tham khảo. .
 

File đính kèm

  • chuyen cot sang dong.xlsm
    19 KB · Đọc: 20
Upvote 0
Tác giả bài #2:
Ở đầu ra, bạn cần 2 mảng:
1. bảng thứ nhất chứa dữ liệu hàng ngang. Mã và mấy cái lình tinh sẽ đi tiếp nối.
2. để làm việc với bảng 1 này, bảng cần một mảng 2 phụ giúp dictionary
Mảng 2 này chứa số cột đã sử dụng cho dòng tương ứng bảng 1
Khi ghi một item mới:
- Dictionary chứa mã (key), item là số dòng +1
- Tương ứng với dòng này trong mảng 1, ghi cột 1 là mã, và cột 2 là cái linh tinh kia.
- Tương ứng với dòng này trong bảng 2, ghi 2 (bạn đã sử dụng 2 cột)
Khi ghi thêm vào một item cũ:
- lấy số dòng qua item
- dò dòng trong bảng 2 lấy được cột đã dùng trong bảng 1
- ghi tăng số cột lên 1
- ghi cái linh tinh vào cột và dòng tương ứng bên mảng 1
Khi xong vòng lặp này, bạn chỉ cần dọn sạch dữ liệu và paste mảng 1 vào sheet

Chú thích mọt mẹo vặt:
Khi ghi xuống mảng 1, bạn đã có cách tìm hàng cuối cùng, nhưng làm cách nào tìm cột lớn nhất?
Giải: dùng kỹ thuật lấy max bên trong vòng lặp.
Cứ mỗi lần update số cột, bạn so nó với cái max hiện tại và update max nếu cần
 
Upvote 0
Ở đầu ra, bạn cần 2 mảng:
Tôi sẽ chỉ dùng 1 mảng đầu ra và 1 biến Max:
Gán Max = 1
Khi add 1 key vào Dic, thì add item = 2:
Dùng item = 2 này làm thứ tự cột cho dòng bắt gặp đầu tiên của từng key. Sau khi add kết quả vào đúng dòng chứa key:
- So sánh item này với Max, để tăng Max lên nếu Max nhỏ hơn.
- Tăng item của key đó lên 1. Sau lần 1 sẽ bằng 3. Dùng 3 làm thứ tự cột cho dòng bắt gặp tiếp theo cùng key

Đây là tận dụng item của Dic thay cho bảng 2 của anh.
 
Upvote 0
Tôi sẽ chỉ dùng 1 mảng đầu ra và 1 biến Max:
Gán Max = 1
Khi add 1 key vào Dic, thì add item = 2:
Dùng item = 2 này làm thứ tự cột cho dòng bắt gặp đầu tiên của từng key. Sau khi add kết quả vào đúng dòng chứa key:
- So sánh item này với Max, để tăng Max lên nếu Max nhỏ hơn.
- Tăng item của key đó lên 1. Sau lần 1 sẽ bằng 3. Dùng 3 làm thứ tự cột cho dòng bắt gặp tiếp theo cùng key

Đây là tận dụng item của Dic thay cho bảng 2 của anh.
Không được, item của Dic như bác nói thì phải chứa dữ liệu kép. 1 phần dùng chứa dòng trong mảng đầu ra, một phần chứa colMx. kỹ thuật này rắc rối và chậm hơn dùng mảng phụ.
 
Upvote 0
Không được, item của Dic như bác nói thì phải chứa dữ liệu kép. 1 phần dùng chứa dòng trong mảng đầu ra, một phần chứa colMx. kỹ thuật này rắc rối và chậm hơn dùng mảng phụ.
Đúng là có rắc rối 1 chút nhưng vẫn được như code sau đây. Gắn dict.keys vào mảng kết quả và so sánh.
Ngoài ra thay cho 2 vòng lặp lồng nhau như bài 2, tôi lặp ngược lại và thêm cái If để giảm thiểu tính toán.

JavaScript:
Sub RowsToCols()
Dim Dict, SArr(), RArr(), TmpArr(), Max As Long, LastRw As Long
Max = 1
Set Dict = CreateObject("Scripting.Dictionary")
With Sheet1
    LastRw = .Cells(1000, 1).End(xlUp).Row
    SArr = Sheet1.Range("A2:B" & LastRw).Value
End With
ReDim TmpArr(1 To LastRw)
For i = 1 To UBound(SArr, 1)
    If Not Dict.Exists(SArr(i, 1)) Then
        k = k + 1
        Dict.Add SArr(i, 1), 2
        TmpArr(k) = k
    End If
Next
ReDim RArr(1 To k, 1 To LastRw)
Key1 = Dict.Keys
For j = 1 To k
    RArr(j, 1) = Key1(j - 1)
Next
For m = 1 To UBound(SArr, 1)
    For n = 1 To k
        If SArr(m, 1) = RArr(n, 1) Then
            y = Dict.Item(SArr(m, 1))
            RArr(n, y) = SArr(m, 2)
            If y > Max Then Max = y
            Dict.Item(SArr(m, 1)) = Dict.Item(SArr(m, 1)) + 1
        End If
    Next n
Next m
Sheet2.Range("A2:AZ100").Clear
Sheet2.[A2].Resize(k, Max).Value = RArr
          
End Sub
 
Upvote 0
Thớt thử 1 cách khác coi. Tiện thể có các chú ở đây. Có thể chỉ giúp cháu với code dưới đây. Có cách nào có thể tối ưu hơn được nữa không? Hoặc thuật toán nào hơn không?
Mã:
Sub ABC()
    Dim Dic As Object, a(), b(), i&, n&, Key, k&
    Set Dic = CreateObject("Scripting.Dictionary")
    With Sheets("Nguon")
        a = .Range("A2:B12").Value
        For i = 1 To UBound(a)
            Dic(a(i, 1)) = Dic(a(i, 1)) & "|" & a(i, 2)
        Next
    End With
    ReDim b(1 To Dic.Count, 1 To 100)
    For Each Key In Dic.keys
        s = Split(Dic.Item(Key), "|")
        k = k + 1
        b(k, 1) = Key
        For n = 1 To UBound(s)
            b(k, n + 1) = s(n)
        Next
        If n > Max Then Max = n
    Next
    Sheets("Ketqua").Range("A7").Resize(Dic.Count, Max).Value = b
End Sub
 
Upvote 0
Cách 2: Dùng
Thớt thử 1 cách khác coi. Tiện thể có các chú ở đây. Có thể chỉ giúp cháu với code dưới đây.
Code ngắn hơn, giải pháp hay, nhưng chưa biết việc xử lý chuỗi liên tục kéo dài có bị chậm không nêu như dữ liệu nhiều.
 
Upvote 0
Giải pháp với 1 vòng lặp:
JavaScript:
Sub PlayWithArray()
Dim SArr, RArr, Dic1
Dim i As Long, s As Long, EndR As Long, n As Long, Max As Long
't = Timer
Set Dic1 = CreateObject("Scripting.Dictionary")
With Dic1
EndR = Sheet1.[a65000].End(xlUp).Row
SArr = Sheet1.Range("A2:B" & EndR).Value
ReDim RArr(1 To UBound(SArr, 1), 1 To 200)
For i = 1 To UBound(SArr, 1)
    If Not .Exists(SArr(i, 1)) Then
        s = s + 1
        .Add SArr(i, 1), s
        RArr(s, 1) = SArr(i, 1)
        RArr(s, 2) = SArr(i, 2)
        RArr(s, 200) = 2
    Else
        n = .Item(SArr(i, 1))
        RArr(n, 200) = RArr(n, 200) + 1
        RArr(n, RArr(n, 200)) = SArr(i, 2)
        If RArr(n, 200) > Max Then Max = RArr(n, 200)
    End If
Next
End With
Sheet2.[A11].Resize(s, Max) = RArr

End Sub
 
Upvote 0
Thớt thử 1 cách khác coi. Tiện thể có các chú ở đây. Có thể chỉ giúp cháu với code dưới đây. Có cách nào có thể tối ưu hơn được nữa không? Hoặc thuật toán nào hơn không?
...
Tôi khác quan điểm với quý vị ở đây về chuyện "tối ưu" nên tôi trả lời theo ngôn ngữ lập trình.

Tôi lập trình theo kiểu cổ điển. Array tính bằng địa chỉ phần tử trong mảng cho nên rất nhanh.
String trong VBA là Immutable (không thay đổi được) cho nên tính sẽ nuốt năng lượng máy.

Mutable/Immutable là thuộc tính người ta dùng để phân biệt loại dữ liệu - từ mutate tiếng Anh là thay đổi:
Mutable là loại dữ liệu đã xác định độ lớn và có thể chứa vào ngăn trong vùng chứa tĩnh, biến chỉ chứa địa chỉ vùng chứa. Mỗi lần cần thay đổi trị, VBA dùng địa chỉ mà update trị trong ngăn chứa. Rất nhanh.
Immutable là loại dữ liệu không thể xác định được độ lớn nên chỉ chứa theo kiểu vùng chứa động. Mỗi lần cần thay đổi trị, VBA phải tìm một vùng nhớ khác cho vừa, và hủy vùng nhớ cũ. Vì phải tìm chỗ chứa mới và hủy chỗ chứa cũ cho nên nó trì trệ một chút.
Ví dụ trong code bạn có s = "abcde"
Sử dụng xong, vài dòng nữa có s = "abcdf"
VBA (dù độ dài bằng nhau) vẫn phải lập một vùng nhớ mới, và chép dữ liệu vào vùng này, hủy địa chỉ cũ trong biến, và ghi địa chỉ mới.
Hầu như tất cả các ngôn ngữ đều nhận thức điều này, bằng cách cải tiến, trong VBA bạn có thể giữ vùng nhớ cũ nếu độ dài bằng nhau hoặc ngắn hơn bằng cách dùng hàm LSET, RSET, MID, và chuỗi tĩnh (fixed length string) - có từ khóa thì tự tìm hiểu nhé, khuôn khổ bài viết không cho tôi nói thêm. (*)

Túm lại, nếu bảo tôi cảu tến tốc độ thì tôi sẽ đầu tiên hết chú ý cách sử dụng mảng của code. Kế đó là cách sử dụng các biến String.

Thuật toán là phương diện khác hoàn toàn. Tôi không đề cập ở đây.

(*) Đã có vài người mắng (từ mắng xéo đến mắng thẳng vào mặt) là tôi là loại "thùng rỗng kêu to" rồi :p. Quý vị có trách sao tôi chỉ dám nói vừa thôi thì chớ trách tôi nhé :p
 
Upvote 0
String trong VBA là Immutable (không thay đổi được) cho nên tính sẽ nuốt năng lượng máy.
Năm xưa tôi còn 1 giải pháp là gắn item của Dic bằng mảng, và mỗi mảng sẽ redim preserve tăng lên 1 dần dần để chứa thêm. Nhưng cách đó chậm bằng 4 lần code bài 10 nên tôi bỏ.
 
Upvote 0
Hoặc thuật toán nào hơn không?
Bạn thử cách Query này xem có hơn không ? Mình không biết thử...
Mã:
let
    Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
    Splited = Table.SplitColumn(Table.Group(Source, {"Tên"}, {{"Ma", each Text.Combine([Mã],"+") }}), "Ma", Splitter.SplitTextByDelimiter("+", QuoteStyle.Csv), {"Ma.1", "Ma.2", "Ma.3", "Ma.4", "Ma.5", "Ma.6"})
in
    Splited
 

File đính kèm

  • Query_chuyen cot sang dong.xlsx
    17.3 KB · Đọc: 4
Upvote 0
Bạn thử cách Query này xem có hơn không ? Mình không biết thử...
Mã:
let
    Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
    Splited = Table.SplitColumn(Table.Group(Source, {"Tên"}, {{"Ma", each Text.Combine([Mã],"+") }}), "Ma", Splitter.SplitTextByDelimiter("+", QuoteStyle.Csv), {"Ma.1", "Ma.2", "Ma.3", "Ma.4", "Ma.5", "Ma.6"})
in
    Splited
Người ta hỏi thẳng là code VBA mờ.
Đây là dân chỉ biết "bấm một phát". Lúc làm Excel thì đã có định kiến sẵn rằng cái gì khó một chút, đi hỏi xin code VBA là đáp án tốt nhất.
Bi giờ bảo họ ra công dùng Power Query, còn phya :p.

Minh chính: ở trên, tôi trả lời cho bài #8. Tác giả là người siêng học hỏi.
 
Upvote 0
Đâu có biết trước tên có nhiều mã nhất là bao nhiêu mã mà xài 6 và đặt 6 cái tên field.
Cái này là học của Anh đó...

Trước đây:
Bỏ xoay: Unpivot Other Columns...
Xong Xoay: Pivot...
Có option: Don't Aggregate.

Bây chừ:
Group lại: Group by...
Tách ra: Split...
Option: Split at ...Each occurrence of the delimiter...

Sản phẩm có thể lỗi do học không đến nơi đến chốn...
 
Upvote 0
Cái này là học của Anh đó...
Hai bài khác nhau. Bài này muốn xác định số cột phải qua mấy bước trung gian như sau, nhưng chỉ là tính toán nên không làm chậm thêm (nếu chậm thì đã chậm rồi).
PHP:
let
    Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
    Group1 = Table.Group(Source, {"Tên"}, {{"Ma", each Text.Combine([Mã],"+") }}),
    Length = Table.AddColumn(Group1,"Len", each Text.Length([Ma]), type number),
    MaxLen = Length[Ma]{List.PositionOf(Length[Len], List.Max(Length[Len]))},
    ColNum = Text.Length(MaxLen) - Text.Length(Text.Replace(MaxLen, "+","")) + 1,
    ColList = Table.AddColumn(Table.FromList({1..ColNum},Splitter.SplitByNothing()),"ColNames", each "Ma" & Text.From([Column1])),
    Splitted = Table.SplitColumn(Group1, "Ma", Splitter.SplitTextByDelimiter("+", QuoteStyle.Csv), ColList[ColNames])
in
    Splitted
File đính kèm tôi thêm 50 ngàn dòng, ra kết quả 18 cột, và tôi đã test 1 dữ liệu khác ra 80 cột

1726022842351.png
 

File đính kèm

  • Query_chuyen cot sang dong.xlsx
    1.7 MB · Đọc: 2
Upvote 0
Thớt thử 1 cách khác coi. Tiện thể có các chú ở đây. Có thể chỉ giúp cháu với code dưới đây. Có cách nào có thể tối ưu hơn được nữa không? Hoặc thuật toán nào hơn không?
Mã:
Sub ABC()
    Dim Dic As Object, a(), b(), i&, n&, Key, k&
    Set Dic = CreateObject("Scripting.Dictionary")
    With Sheets("Nguon")
        a = .Range("A2:B12").Value
        For i = 1 To UBound(a)
            Dic(a(i, 1)) = Dic(a(i, 1)) & "|" & a(i, 2)
        Next
    End With
    ReDim b(1 To Dic.Count, 1 To 100)
    For Each Key In Dic.keys
        s = Split(Dic.Item(Key), "|")
        k = k + 1
        b(k, 1) = Key
        For n = 1 To UBound(s)
            b(k, n + 1) = s(n)
        Next
        If n > Max Then Max = n
    Next
    Sheets("Ketqua").Range("A7").Resize(Dic.Count, Max).Value = b
End Sub
Dữ liệu đã dược sort theo tên, không nên dùng dictionary, chỉ 1 vòng For là xong.
 
Upvote 0
Dữ liệu đã dược sort theo tên, không nên dùng dictionary, chỉ 1 vòng For là xong.
Đúng như Thầy nói. có lẽ viết 1 vòng lặp có lẽ gọn gàng hơn nếu như dữ liệu được sắp xếp trước. Không biết có bị sót lỗi nào có thể xảy ra nữa không?
Mã:
Sub XYZ()
    Dim a(), b(), i&, n&, tam, k&, Max&
    With Sheets("Nguon")
        a = .Range("A2:B12").Value
        ReDim b(1 To UBound(a), 1 To 100)
        For i = 1 To UBound(a)
            If a(i, 1) <> tam Then
                tam = a(i, 1): k = k + 1: n = 1
            End If
            n = n + 1
            b(k, 1) = tam
            b(k, n) = a(i, 2)
            If n > Max Then Max = n
        Next
    End With
    Sheets("Ketqua").Range("A7").Resize(k, Max).Value = b
End Sub
 
Upvote 0
Đúng như Thầy nói. có lẽ viết 1 vòng lặp có lẽ gọn gàng hơn nếu như dữ liệu được sắp xếp trước. Không biết có bị sót lỗi nào có thể xảy ra nữa không?
Mã:
Sub XYZ()
    Dim a(), b(), i&, n&, tam, k&, Max&
    With Sheets("Nguon")
        a = .Range("A2:B12").Value
        ReDim b(1 To UBound(a), 1 To 100)
        For i = 1 To UBound(a)
            If a(i, 1) <> tam Then
                tam = a(i, 1): k = k + 1: n = 1
            End If
            n = n + 1
            b(k, 1) = tam
            b(k, n) = a(i, 2)
            If n > Max Then Max = n
        Next
    End With
    Sheets("Ketqua").Range("A7").Resize(k, Max).Value = b
End Sub
a = .Range("A2:B12").Value. Nếu dữ liệu quá dòng 12 thì sao?
ReDim b(1 To UBound(a), 1 To 100). Có thể kết quả lớn hơn 100 cột, thêm lệnh tăng số cột khi max> ubound(a,2) trước khi gán kết quả vào b
 
Upvote 0
Web KT

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

Back
Top Bottom