Chuyển dữ liệu dòng thành cột, có bỏ qua giá trị bằng 0 (1 người xem)

Liên hệ QC

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

Minh Ngọc LH

Thành viên chính thức
Tham gia
14/7/18
Bài viết
71
Được thích
32
Giới tính
Nữ
Chào các anh chị
Giúp em tình huống này với ạ. Em muốn chuyển dữ liệu từ dòng thành cột và bỏ qua những giá trị bằng 0 (chi tiết có ghi trong file đính kèm)
Hàng ngày em phải làm việc này thủ công mà rất tốn thời gian
sheet DATA là dữ liệu gốc, sheet KETQUA là kết quả mong muốn
Em cảm ơn nhiều
 

File đính kèm

Chào các anh chị
Giúp em tình huống này với ạ. Em muốn chuyển dữ liệu từ dòng thành cột và bỏ qua những giá trị bằng 0 (chi tiết có ghi trong file đính kèm)
Hàng ngày em phải làm việc này thủ công mà rất tốn thời gian
sheet DATA là dữ liệu gốc, sheet KETQUA là kết quả mong muốn
Em cảm ơn nhiều
Bạn cho thêm dữ liệu ở sheets CHUYEN DONG -> COT.Thêm mấy ngày nữa để làm cho nó chuẩn.
 
Upvote 0

File đính kèm

Upvote 0
Chào các anh chị
Giúp em tình huống này với ạ. Em muốn chuyển dữ liệu từ dòng thành cột và bỏ qua những giá trị bằng 0 (chi tiết có ghi trong file đính kèm)
Hàng ngày em phải làm việc này thủ công mà rất tốn thời gian
sheet DATA là dữ liệu gốc, sheet KETQUA là kết quả mong muốn
Em cảm ơn nhiều
Bạn chạ̣̣y thử code này xem sao
Mã:
Sub Rows_Columns()
Dim Data
Dim Res
Dim i, j, k, x, z, t
Data = Sheet1.Range("b2", Sheet1.Range("r1000000").End(xlUp))
With CreateObject("Scripting.Dictionary")
    For x = 3 To UBound(Data) - 34 Step 37
        For i = x To x + 34
            For j = 2 To UBound(Data, 2)
                If Data(i, j) <> 0 Then
                    k = k + 1
                    .Item(k) = Array(Data(x - 1, j), Data(x - 2, 2), Data(i, 1), Data(i, j))
                End If
            Next j
        Next i
    Next x
    ReDim Res(1 To .Count, 1 To 4)
    For Each i In .keys
        Res(i, 1) = .Item(i)(0)
        Res(i, 2) = .Item(i)(1)
        Res(i, 3) = .Item(i)(2)
        Res(i, 4) = .Item(i)(3)
    Next i
End With
With Sheet3
    .Range("n2").Resize(UBound(Res), 4) = Res
End With
End Sub
 
Upvote 0
Bạn chạ̣̣y thử code này xem sao
Mã:
Sub Rows_Columns()
Dim Data
Dim Res
Dim i, j, k, x, z, t
Data = Sheet1.Range("b2", Sheet1.Range("r1000000").End(xlUp))
With CreateObject("Scripting.Dictionary")
    For x = 3 To UBound(Data) - 34 Step 37
        For i = x To x + 34
            For j = 2 To UBound(Data, 2)
                If Data(i, j) <> 0 Then
                    k = k + 1
                    .Item(k) = Array(Data(x - 1, j), Data(x - 2, 2), Data(i, 1), Data(i, j))
                End If
            Next j
        Next i
    Next x
    ReDim Res(1 To .Count, 1 To 4)
    For Each i In .keys
        Res(i, 1) = .Item(i)(0)
        Res(i, 2) = .Item(i)(1)
        Res(i, 3) = .Item(i)(2)
        Res(i, 4) = .Item(i)(3)
    Next i
End With
With Sheet3
    .Range("n2").Resize(UBound(Res), 4) = Res
End With
End Sub
Nếu trường hợp số lượng các bộ phận thay đổi giữa các ngày thì sao bạn nhỉ?
Tôi giả sử trường hợp
- Ngày 15/11/2018 có 35 bộ phận
- Ngày 16/11/2018 có 36 bộ phận (thành lập mới 1 bộ phận)
- Ngày 15/12/2018 có 34 bộ phận (giải thể 2 bộ phận cũ)
 
Upvote 0
Nếu trường hợp số lượng các bộ phận thay đổi giữa các ngày thì sao bạn nhỉ?
Tôi giả sử trường hợp
- Ngày 15/11/2018 có 35 bộ phận
- Ngày 16/11/2018 có 36 bộ phận (thành lập mới 1 bộ phận)
- Ngày 15/12/2018 có 34 bộ phận (giải thể 2 bộ phận cũ)
File mẫu có sao làm vậy mà bạn.
 
Upvote 0
File mẫu có sao làm vậy mà bạn.
À, tôi thấy code của bạn chạy hoàn toàn chính xác rồi.
Chỉ là tôi muốn tìm hiểu xem có cách nào để làm tổng quát hơn được hay không?
Tôi nghĩ trường hợp tôi đưa ra có thể sẽ gặp trong thực tế.
 
Upvote 0
Nếu trường hợp số lượng các bộ phận thay đổi giữa các ngày thì sao bạn nhỉ?
Tôi giả sử trường hợp
- Ngày 15/11/2018 có 35 bộ phận
- Ngày 16/11/2018 có 36 bộ phận (thành lập mới 1 bộ phận)
- Ngày 15/12/2018 có 34 bộ phận (giải thể 2 bộ phận cũ)
À, tôi thấy code của bạn chạy hoàn toàn chính xác rồi.
Chỉ là tôi muốn tìm hiểu xem có cách nào để làm tổng quát hơn được hay không?
Tôi nghĩ trường hợp tôi đưa ra có thể sẽ gặp trong thực tế.
Làm sao gặp trong thực tế khi 1 ngày thêm, 1 ngày thêm 2 bộ phận vậy ? giải thể xóa sổ, thành lập nhanh thế

Còn bài này có thể khỏi DIC cần 2 FOR thôi, dĩ nhiên mảng Kết quả phải chấp nhận khai báo trước kích cỡ - có thể lãng phí bộ nhớ, nhưng nếu bài toán thực tế nhỏ vừa thì nên thực dụng như thế
 
Lần chỉnh sửa cuối:
Upvote 0
Ngó qua thì 3 vòng lặp là đủ nhiều rồi.
Em nhờ anh xem giúp em code này nhé!
Em muốn tổng quát hơn nên viết vậy
PHP:
Sub GPE()
    Dim I As Long, J As Long, K As Long, C As Long, Col As Long, lR As Long
    Dim Rng As Range, Dat As Date
    Dim Arr, sArr(), Res()
    
    With Sheet1
        Set Rng = .Range("B2", .Range("B2").End(xlDown))
        Col = .Cells(3, Columns.Count).End(xlToLeft).Column - 1
        ReDim Res(1 To Rng.Rows.Count * Col, 1 To 4)
        Arr = FindArray(Rng, "Ngày", True)
        
        For I = LBound(Arr) To UBound(Arr)
            Dat = CDate(.Range(Arr(I)).Offset(, 1))
            lR = .Range(Arr(I)).Offset(1, 2).End(xlDown).Row
            sArr() = .Range(.Range(Arr(I)).Offset(1), .Range("B" & lR)).Resize(, Col).Value
        
            For J = 2 To UBound(sArr, 1)
                For C = 2 To UBound(sArr, 2)
                    If sArr(J, C) Then
                        K = K + 1
                        Res(K, 1) = sArr(1, C): Res(K, 2) = Dat
                        Res(K, 3) = sArr(J, 1): Res(K, 4) = sArr(J, C)
                    End If
                Next C
            Next J
        Next I
    End With
    
    If K Then
        Sheet3.Range("B2").Resize(K, 4) = Res
    End If
End Sub

Function FindArray(FindArea As Range, SearchStr As String, Optional Arrange As Boolean = True)
    Dim ArrayList As Object
    Dim MyResults() As String
    Dim n As Long, I As Long
    Dim aCell As Range, bCell As Range, ExitLoop
    
    Set aCell = FindArea.Find(What:=SearchStr, LookIn:=xlValues, _
            LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
            MatchCase:=False, SearchFormat:=False)
    
    If Not aCell Is Nothing Then
        Set bCell = aCell
    
        ReDim Preserve MyResults(n + 1)
        MyResults(n) = aCell.Address
        n = n + 1
    
        Do While ExitLoop = False
            Set aCell = FindArea.FindNext(after:=aCell)
    
            If Not aCell Is Nothing Then
                If aCell.Address = bCell.Address Then Exit Do
                ReDim Preserve MyResults(n)
                MyResults(n) = aCell.Address
                n = n + 1
            Else
                ExitLoop = True
            End If
        Loop
    Else
        Exit Function
    End If
    
    Set ArrayList = CreateObject("System.Collections.Arraylist")
    If Arrange Then
        For I = LBound(MyResults) To UBound(MyResults)
            ArrayList.Add MyResults(I)
        Next I
        ArrayList.Sort
        FindArray = ArrayList.ToArray
        Set ArrayList = Nothing
    Else
        FindArray = MyResults
    End If
    
    Set aCell = Nothing: Set bCell = Nothing
End Function
 
Upvote 0
Làm sao gặp trong thực tế khi 1 ngày thêm, 1 ngày thêm 2 bộ phận vậy ? giải thể xóa sổ, thành lập nhanh thế

Còn bài này có thể khỏi DIC cần 2 FOR thôi, dĩ nhiên mảng Kết quả phải chấp nhận khai báo trước kích cỡ - có thể lãng phí bộ nhớ, nhưng nếu bài toán thực tế nhỏ vừa thì nên thực dụng như thế
Chắc vậy, bài này 2 vòng lặp vẫn được, chỉ đáng ngại cái là kích thước mảng kết quả
 
Upvote 0
@Minh Ngọc LH
Thử với 2 vòng lăp, không dùng Dic
Mã:
Sub Rows_Columns_()
Dim Data
Dim Res
Dim i, j, k, r
Data = Sheet1.Range("b2", Sheet1.Range("r1000000").End(xlUp))
ReDim Res(1 To UBound(Data) * UBound(Data, 2), 1 To 4)
For i = 2 To UBound(Data)
    If IsNumeric(Data(i - 1, 2)) = False And IsNumeric(Data(i, 2)) = True Then r = i
    If IsNumeric(Data(i, 2)) = True Then
        For j = 2 To UBound(Data, 2)
            If Data(i, j) <> 0 Then
                k = k + 1
                Res(k, 1) = Data(r - 1, j)
                Res(k, 2) = Data(r - 2, 2)
                Res(k, 3) = Data(i, 1)
                Res(k, 4) = Data(i, j)
            End If
        Next j
    End If
Next i
With Sheet3
    .Range("s2").Resize(k, 4) = Res
End With
End Sub
 
Upvote 0
Bạn chạ̣̣y thử code này xem sao
Mã:
Sub Rows_Columns()
Dim Data
Dim Res
Dim i, j, k, x, z, t
Data = Sheet1.Range("b2", Sheet1.Range("r1000000").End(xlUp))
With CreateObject("Scripting.Dictionary")
    For x = 3 To UBound(Data) - 34 Step 37
        For i = x To x + 34
            For j = 2 To UBound(Data, 2)
                If Data(i, j) <> 0 Then
                    k = k + 1
                    .Item(k) = Array(Data(x - 1, j), Data(x - 2, 2), Data(i, 1), Data(i, j))
                End If
            Next j
        Next i
    Next x
    ReDim Res(1 To .Count, 1 To 4)
    For Each i In .keys
        Res(i, 1) = .Item(i)(0)
        Res(i, 2) = .Item(i)(1)
        Res(i, 3) = .Item(i)(2)
        Res(i, 4) = .Item(i)(3)
    Next i
End With
With Sheet3
    .Range("n2").Resize(UBound(Res), 4) = Res
End With
End Sub
Cảm ơn anh nhiều, hoàn toàn chính xác ạ
Bài đã được tự động gộp:

Em nhờ anh xem giúp em code này nhé!
Em muốn tổng quát hơn nên viết vậy
PHP:
Sub GPE()
    Dim I As Long, J As Long, K As Long, C As Long, Col As Long, lR As Long
    Dim Rng As Range, Dat As Date
    Dim Arr, sArr(), Res()
   
    With Sheet1
        Set Rng = .Range("B2", .Range("B2").End(xlDown))
        Col = .Cells(3, Columns.Count).End(xlToLeft).Column - 1
        ReDim Res(1 To Rng.Rows.Count * Col, 1 To 4)
        Arr = FindArray(Rng, "Ngày", True)
       
        For I = LBound(Arr) To UBound(Arr)
            Dat = CDate(.Range(Arr(I)).Offset(, 1))
            lR = .Range(Arr(I)).Offset(1, 2).End(xlDown).Row
            sArr() = .Range(.Range(Arr(I)).Offset(1), .Range("B" & lR)).Resize(, Col).Value
       
            For J = 2 To UBound(sArr, 1)
                For C = 2 To UBound(sArr, 2)
                    If sArr(J, C) Then
                        K = K + 1
                        Res(K, 1) = sArr(1, C): Res(K, 2) = Dat
                        Res(K, 3) = sArr(J, 1): Res(K, 4) = sArr(J, C)
                    End If
                Next C
            Next J
        Next I
    End With
   
    If K Then
        Sheet3.Range("B2").Resize(K, 4) = Res
    End If
End Sub

Function FindArray(FindArea As Range, SearchStr As String, Optional Arrange As Boolean = True)
    Dim ArrayList As Object
    Dim MyResults() As String
    Dim n As Long, I As Long
    Dim aCell As Range, bCell As Range, ExitLoop
   
    Set aCell = FindArea.Find(What:=SearchStr, LookIn:=xlValues, _
            LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
            MatchCase:=False, SearchFormat:=False)
   
    If Not aCell Is Nothing Then
        Set bCell = aCell
   
        ReDim Preserve MyResults(n + 1)
        MyResults(n) = aCell.Address
        n = n + 1
   
        Do While ExitLoop = False
            Set aCell = FindArea.FindNext(after:=aCell)
   
            If Not aCell Is Nothing Then
                If aCell.Address = bCell.Address Then Exit Do
                ReDim Preserve MyResults(n)
                MyResults(n) = aCell.Address
                n = n + 1
            Else
                ExitLoop = True
            End If
        Loop
    Else
        Exit Function
    End If
   
    Set ArrayList = CreateObject("System.Collections.Arraylist")
    If Arrange Then
        For I = LBound(MyResults) To UBound(MyResults)
            ArrayList.Add MyResults(I)
        Next I
        ArrayList.Sort
        FindArray = ArrayList.ToArray
        Set ArrayList = Nothing
    Else
        FindArray = MyResults
    End If
   
    Set aCell = Nothing: Set bCell = Nothing
End Function
Mấy anh giỏi thiệt :)
Bài đã được tự động gộp:

@Minh Ngọc LH
Thử với 2 vòng lăp, không dùng Dic
Mã:
Sub Rows_Columns_()
Dim Data
Dim Res
Dim i, j, k, r
Data = Sheet1.Range("b2", Sheet1.Range("r1000000").End(xlUp))
ReDim Res(1 To UBound(Data) * UBound(Data, 2), 1 To 4)
For i = 2 To UBound(Data)
    If IsNumeric(Data(i - 1, 2)) = False And IsNumeric(Data(i, 2)) = True Then r = i
    If IsNumeric(Data(i, 2)) = True Then
        For j = 2 To UBound(Data, 2)
            If Data(i, j) <> 0 Then
                k = k + 1
                Res(k, 1) = Data(r - 1, j)
                Res(k, 2) = Data(r - 2, 2)
                Res(k, 3) = Data(i, 1)
                Res(k, 4) = Data(i, j)
            End If
        Next j
    End If
Next i
With Sheet3
    .Range("s2").Resize(k, 4) = Res
End With
End Sub
Tốc độ chạy code rất nhanh ạ, có lẽ dữ liệu ít nên không thấy khác biệt nhiều khi sử dụng dic hoặc không có dic. Cảm ơn anh một lần nữa
 
Lần chỉnh sửa cuối:
Upvote 0
ReDim Res(1 To UBound(Data) * UBound(Data, 2), 1 To 4)
'...
k = k + 1
Nên kiểm tra UBound(Data) * UBound(Data, 2) và biến k so với số dòng tối đa của bảng tính.

Tạm có 2 cách xử lý trường hợp nhiều kết quả > số dòng tối đa của bảng tính:
- Cách 1: Chỉ lấy kết quả tối đa bằng số lượng dòng tối đa của bảng tính. Giả sử Excel 2007 trở lên, lấy 1048500 kết quả.
PHP:
'Trên đầu Sub
Const maxKQ as Long =1048500
'..
ReDim Res(1 To maxKQ, 1 To 4) 
'..
if k>maxKQ then msgbox "Nhieu ket qua roi!": Goto nextCode
'...
nextCode:
With Sheet3
    if k>0 then .Range("s2").Resize(k, 4) = Res
End With

- Cách 2: Lấy toàn bộ kết quả. Giả sử mỗi lượt điền vào 1048500 dòng. Vậy sau mỗi lượt thì điền sang 4 cột mới. Hết bảng tính thì điền sang bảng tính mới. Tạm một workbook đã.
 
Upvote 0
Bạn chạ̣̣y thử code này xem sao
Mã:
Sub Rows_Columns()
Dim Data
Dim Res
Dim i, j, k, x, z, t
Data = Sheet1.Range("b2", Sheet1.Range("r1000000").End(xlUp))
With CreateObject("Scripting.Dictionary")
    For x = 3 To UBound(Data) - 34 Step 37
        For i = x To x + 34
            For j = 2 To UBound(Data, 2)
                If Data(i, j) <> 0 Then
                    k = k + 1
                    .Item(k) = Array(Data(x - 1, j), Data(x - 2, 2), Data(i, 1), Data(i, j))
                End If
            Next j
        Next i
    Next x
    ReDim Res(1 To .Count, 1 To 4)
    For Each i In .keys
        Res(i, 1) = .Item(i)(0)
        Res(i, 2) = .Item(i)(1)
        Res(i, 3) = .Item(i)(2)
        Res(i, 4) = .Item(i)(3)
    Next i
End With
With Sheet3
    .Range("n2").Resize(UBound(Res), 4) = Res
End With
End Sub
Cái Dictionary này nó khó khó làm sao ấy, em đọc lý thuyết mãi mà không áp dụng được chút nào, hì hì.
Anh hoặc ai đó tốt bụng có thể hướng dẫn cách vận hành nó được không ạ
 
Upvote 0
Cái Dictionary này nó khó khó làm sao ấy, em đọc lý thuyết mãi mà không áp dụng được chút nào, hì hì.
Anh hoặc ai đó tốt bụng có thể hướng dẫn cách vận hành nó được không ạ
Dictionary đai khái có thể hình dung như 1 mảng 2 chiều có cột dầu tiên chứa các phân tử duy nhất không trùng.
Ví du như file bài 1 của bạn, nếu cột A đươc đánh số thứ tự từ dòng 2 đến hết, có thể coi như 1 dictionary, trong đó :
Nếu chon 1 số thứ tự bất kỳ, bạn sẽ có các thông số tương ứng từ cột C:R cùng dòng.
Số thứ tự bât kỳ chính là 1 key, các thông số từ cột C:R cùng dòng là 1 Item tương ứng
=>Keys : Là mảng số thứ tự tại cột A, các phần tử trong cột là duy nhất.
=>Items : Là mảng có số phần tử đúng bằng số lượng tai cột A, mỗi phần tử chứa các thông số từ cột C:R cùng dòng tương ứng
Bạn xem thử code duới đây xem có nắm được phần nào
Mã:
Sub VDDic_()
With CreateObject("Scripting.Dictionary")
    .Add 1, "a"
    .Add 2, "b"
    .Add 3, "c"
    Sheet1.Range("a1").Resize(1, .Count) = .keys
    Sheet1.Range("a3").Resize(1, .Count) = .items
End With
End Sub
Bạn có thể thay a, b, c thành số, thành mang 1 chiều rồi xuất ra xem thử

Đại khái Dic hình dung như trên, các phương thức & thuộc tính bạn áp dung dần sẽ ra.
 
Lần chỉnh sửa cuối:
Upvote 0
Cái Dictionary này nó khó khó làm sao ấy, em đọc lý thuyết mãi mà không áp dụng được chút nào, hì hì.
Anh hoặc ai đó tốt bụng có thể hướng dẫn cách vận hành nó được không ạ
Đại khái Dic khi dùng là muốn lưu lại cái gì đó vào Item nào đó.Rồi khi cần thiết thì lấy ra dùng thôi.Ví dụ bạn có thể lưu vị trí của phần tử trong mảng.Lưu giá trị của phần tử cùng chiều với nó.vv
 
Upvote 0
Dictionary đai khái có thể hình dung như 1 mảng 2 chiều có cột dầu tiên chứa các phân tử duy nhất không trùng.
Ví du như file bài 1 của bạn, nếu cột A đươc đánh số thứ tự từ dòng 2 đến hết, có thể coi như 1 dictionary, trong đó :
Nếu chon 1 số thứ tự bất kỳ, bạn sẽ có các thông số tương ứng từ cột C:R cùng dòng.
Số thứ tự bât kỳ chính là 1 key, các thông số từ cột C:R cùng dòng là 1 Item tương ứng
=>Keys : Là mảng số thứ tự tại cột A, các phần tử trong cột là duy nhất.
=>Items : Là mảng có số phần tử đúng bằng số lượng tai cột A, mỗi phần tử chứa các thông số từ cột C:R cùng dòng tương ứng
Bạn xem thử code duới đây xem có nắm được phần nào
Mã:
Sub VDDic_()
With CreateObject("Scripting.Dictionary")
    .Add 1, "a"
    .Add 2, "b"
    .Add 3, "c"
    Sheet1.Range("a1").Resize(1, .Count) = .keys
    Sheet1.Range("a3").Resize(1, .Count) = .items
End With
End Sub
Bạn có thể thay a, b, c thành số, thành mang 1 chiều rồi xuất ra xem thử

Đại khái Dic hình dung như trên, các phương thức & thuộc tính bạn áp dung dần sẽ ra.
Cảm ơn anh nhiều. Em sẽ ngâm cứu thêm. Đúng là phải có người chỉ dẫn thì mới hiểu nhanh được
 
Upvote 0
Web KT

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

Back
Top Bottom