Страницы: 1
RSS
Работа с массивами данных vba, Сортировка, вывод уникальных значений
 
Добрый день!
Прошу помочь в решении вопроса: как с помощью vba вывести на лист уникальные значения сформированного ранее массива, отсортированные по возрастанию?
Лист "Для заполнения" содержит реестр, в котором указано, когда кто сколько и где работал. На листе "Табель" необходимо вывести сформированный табель на основании этого реестра.
План такой: по номеру месяца, указанному на листе "Табель", найти соответствующие строки на листе "Для заполнения". Вычленить из даты дни, вывести их по порядку в строку с названиями столбцов. Далее вывести в соответствующие столбцы данные по сотрудникам (фио уникальные, должность и т.д.), затем уже заполнить получившуюся таблицу значениями по количеству отработанного времени.

P.S.: пробовала решить данную задачу с помощью сводной таблицы, но есть затык: в полях значений числовые данные суммируются, а значения типа ОТ (отпуск) или Б (болел)  отражаются как 0, и не получается перенести их в сводную в буквенном виде. Если этот затык можно решить, то можно обойтись без макроса.
 
Цитата
написал:
Работа с массивами данных vba
читаем последнее предложение
Цитата
написал:
то можно обойтись без макроса.
VBA без макроса - это как свадьба без невесты!
Программисты - это люди, решающие проблемы, о существовании которых Вы не подозревали, методами, которых Вы не понимаете!
 
Цитата
написал:
VBA без макроса - это как свадьба без невесты!
Мне нужно сформировать табель на основании реестра. С помощью сводной таблицы у меня не получилось, поэтому хочу использовать макрос. Но если можно обойтись без макроса, а просто допилить сводную - буду только рада.

Я начиталась уже на просторах интернета про коллекции, с которыми никогда не работала, не представляю, что это такое и с чем это едят, поэтому ищу максимально простое и понятное решение
Изменено: Елена Дроздова - 16.11.2022 11:13:31
 
Елена Дроздова, Зачем изобретать. когда уже есть велосипед
 
Код
Sub FillTabel()
    Dim arr As Variant
    arr = GetArrTabel()
    If IsEmpty(arr) Then Exit Sub
    
    With Sheets("Табель").Cells(4, 1).Resize(UBound(arr, 1), UBound(arr, 2))
        .Value = arr
    End With
End Sub

Private Function GetArrTabel()
    Dim tb As ListObject
    Set tb = Sheets("Для заполнения").ListObjects("Заполнение")

    Dim fio As Variant
    Dim dlj As Variant
    Dim cat As Variant
    Dim nom As Variant
    
    fio = tb.ListColumns("ФИО").Range
    dlj = tb.ListColumns("Должность").Range
    cat = tb.ListColumns("Категория персонала").Range
    nom = tb.ListColumns("Табельный номер").Range
    
    Dim sKey As String
    Dim arr As Variant
    ReDim arr(1 To UBound(fio, 1), 1 To 5)
    
    Dim dic As Object
    Set dic = CreateObject("Scripting.Dictionary")
    
    Dim yy As Long
    Dim uu As Long
    For yy = 2 To UBound(fio, 1)
        sKey = Join(Array(nom(yy, 1), fio(yy, 1)), vbTab)
        
        If Not dic.Exists(sKey) Then
            dic.Item(sKey) = 0
            uu = uu + 1
            arr(uu, 1) = uu
            arr(uu, 2) = fio(yy, 1)
            arr(uu, 3) = dlj(yy, 1)
            arr(uu, 4) = cat(yy, 1)
            arr(uu, 5) = nom(yy, 1)
        End If
    Next
    
    If uu > 0 Then
        Dim brr As Variant
        ReDim brr(1 To uu, 1 To UBound(arr, 2))
        For yy = 1 To UBound(brr, 1)
            For uu = 1 To UBound(brr, 2)
                brr(yy, uu) = arr(yy, uu)
            Next
        Next
        GetArrTabel = brr
    End If
End Function
 
Код
Sub FillTabel()
  Dim a, r&, rg As Range
  Worksheets("Табель").Activate
  r = Cells(Rows.Count, 1).End(xlUp).Row
  If r > 3 Then Rows(4).Resize(r - 3).ClearContents
  With Worksheets("Для заполнения")
    Set rg = .Range(.Cells(2, 3), .Cells(Rows.Count, 3).End(xlUp))
  End With
  [b4].Resize(rg.Count, 4) = rg.Resize(rg.Count, 4).Value
  [b4].CurrentRegion.RemoveDuplicates Columns:=2, Header:=xlYes
  ReDim a(1 To [b4].CurrentRegion.Rows.Count - 1, 1 To 1)
  For r = 1 To UBound(a): a(r, 1) = r: Next
  [a4].Resize(UBound(a), 1) = a
End Sub
Изменено: Ігор Гончаренко - 16.11.2022 11:44:09
Программисты - это люди, решающие проблемы, о существовании которых Вы не подозревали, методами, которых Вы не понимаете!
 
МатросНаЗебре, если не трудно, прокомментируйте, пожалуйста, происходящее в коде
 
Цитата
написал:
вывести на лист уникальные значения сформированного ранее массива, отсортированные по возрастанию
Код
'v2
Sub FillTabel()
    Dim arr As Variant
    arr = GetArrTabel() 'Получаем массив для вывода
    If IsEmpty(arr) Then Exit Sub   'Если массива нет, выходим из макроса
    
    With Sheets("Табель").Cells(4, 1).Resize(UBound(arr, 1), UBound(arr, 2))    'Диапазон на листе Табель
        .Value = arr    'Выводим массив
        SortRange .Cells, 2 'Сортируем
        FillNumber .Columns(1)  'Заполняем номер, первый столбец.
    End With
End Sub

Private Function GetArrTabel()
'Функция, формирующая массив табеля
    Dim tb As ListObject
    Set tb = Sheets("Для заполнения").ListObjects("Заполнение") 'Получаем умную таблицу на листе Для заполнения

    'Запоминаем в массивы столбцы умной таблицы
    Dim fio As Variant
    Dim dlj As Variant
    Dim cat As Variant
    Dim nom As Variant
    
    fio = tb.ListColumns("ФИО").Range
    dlj = tb.ListColumns("Должность").Range
    cat = tb.ListColumns("Категория персонала").Range
    nom = tb.ListColumns("Табельный номер").Range
    
    'Ключ для словаря для удаления дубликатов
    Dim sKey As String
    
    'Промежуточный массив с количеством строк, равным количеству строк в умной таблице
    Dim arr As Variant
    ReDim arr(1 To UBound(fio, 1), 1 To 5)
    
    'Cловарь для удаления дубликатов
    Dim dic As Object
    Set dic = CreateObject("Scripting.Dictionary")
    
    Dim yy As Long
    Dim uu As Long
    For yy = 2 To UBound(fio, 1)
        'Ключ для удаления дубликатов. Учитываемые столбцы можно менять.
        sKey = Join(Array(nom(yy, 1), fio(yy, 1)), vbTab)
        
        If Not dic.Exists(sKey) Then    'Если ключа ещё нет
            dic.Item(sKey) = 0  'Добавляем ключ в словарь
            uu = uu + 1         'Номер строки. Отличается от y на дубликаты.
            arr(uu, 1) = uu
            arr(uu, 2) = fio(yy, 1) 'ФИО
            arr(uu, 3) = dlj(yy, 1) 'Должность
            arr(uu, 4) = cat(yy, 1) 'Категория
            arr(uu, 5) = nom(yy, 1) 'Табельный номер
        End If
    Next
    
    If uu > 0 Then  'Если в промежуточном массиве что-то есть
        'Массив для вывода. Тот же массив, что и промежуточный, только без пустых строк.
        Dim brr As Variant
        ReDim brr(1 To uu, 1 To UBound(arr, 2))
        For yy = 1 To UBound(brr, 1)
            For uu = 1 To UBound(brr, 2)
                brr(yy, uu) = arr(yy, uu)
            Next
        Next
        GetArrTabel = brr
    End If
End Function

Private Sub SortRange(rr As Range, xSort As Long)
'Процедура, сортирующая диапазон
     With rr.Parent.Sort
        .SortFields.Clear
        .SortFields.Add Key:=rr.Columns(xSort), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
        .SetRange rr: .Header = xlGuess: .MatchCase = False: .Orientation = xlTopToBottom: .SortMethod = xlPinYin
        .Apply
    End With
End Sub

Private Sub FillNumber(rr As Range)
'Заполнение номера, не табельного.
    If rr.Rows.Count = 1 Then
        rr.Cells(1, 1).Value = 1
    Else
        Dim arr As Variant
        arr = rr.Columns(1).Value
        Dim yy As Long
        For yy = 1 To UBound(arr, 1)
            arr(yy, 1) = yy
        Next
        rr.Columns(1).Value = arr
    End If
End Sub

Изменено: МатросНаЗебре - 16.11.2022 11:44:42
 
Ігор Гончаренко, Ваш код выводит не уникальные значения. Одна строка ФИО может повторяться только один раз и должна быть отсортирована по возрастанию
Изменено: Елена Дроздова - 16.11.2022 11:44:57
 
МатросНаЗебре, спасибо, работает!

Но не все работает. Выбора нужного месяца здесь нет, правильно?
Изменено: Елена Дроздова - 16.11.2022 11:56:39
 
МатросНаЗебре, мне нужно еще дни вывести в строку с названиями столбцов - как это сделать?
 
С выбором месяца.
Код
'v3
Sub FillTabel()
    Dim arr As Variant
    arr = GetArrTabel(Sheets("Табель").Cells(1, 2).Value) 'Получаем массив для вывода
    If IsEmpty(arr) Then Exit Sub   'Если массива нет, выходим из макроса
    
    With Sheets("Табель").Cells(4, 1).Resize(UBound(arr, 1), UBound(arr, 2))    'Диапазон на листе Табель
        .Value = arr    'Выводим массив
        SortRange .Cells, 2 'Сортируем
        FillNumber .Columns(1)  'Заполняем номер, первый столбец.
    End With
End Sub

Private Function GetArrTabel(monthTabel As Byte)
'Функция, формирующая массив табеля
    Dim tb As ListObject
    Set tb = Sheets("Для заполнения").ListObjects("Заполнение") 'Получаем умную таблицу на листе Для заполнения

    'Запоминаем в массивы столбцы умной таблицы
    Dim fio As Variant
    Dim dlj As Variant
    Dim cat As Variant
    Dim nom As Variant
    Dim dat As Variant
    
    With tb
        fio = .ListColumns("ФИО").Range
        dlj = .ListColumns("Должность").Range
        cat = .ListColumns("Категория персонала").Range
        nom = .ListColumns("Табельный номер").Range
        dat = .ListColumns("Дата").Range
    End With
    
    'Ключ для словаря для удаления дубликатов
    Dim sKey As String
    
    'Промежуточный массив с количеством строк, равным количеству строк в умной таблице
    Dim arr As Variant
    ReDim arr(1 To UBound(fio, 1), 1 To 5)
    
    'Cловарь для удаления дубликатов
    Dim dic As Object
    Set dic = CreateObject("Scripting.Dictionary")
    
    Dim flag As Boolean
    Dim yy As Long
    Dim uu As Long
    For yy = 2 To UBound(fio, 1)
        flag = False
        If IsDate(dat(yy, 1)) Then
            If Month(dat(yy, 1)) = monthTabel Then
                flag = True
            End If
        End If
        If flag Then
            'Ключ для удаления дубликатов. Учитываемые столбцы можно менять.
            sKey = Join(Array(nom(yy, 1), fio(yy, 1)), vbTab)
            If dic.Exists(sKey) Then flag = False
        End If
        If flag Then    'Если ключа ещё нет
            dic.Item(sKey) = 0  'Добавляем ключ в словарь
            uu = uu + 1         'Номер строки. Отличается от y на дубликаты.
            arr(uu, 1) = uu
            arr(uu, 2) = fio(yy, 1) 'ФИО
            arr(uu, 3) = dlj(yy, 1) 'Должность
            arr(uu, 4) = cat(yy, 1) 'Категория
            arr(uu, 5) = nom(yy, 1) 'Табельный номер
        End If
    Next
    
    If uu > 0 Then  'Если в промежуточном массиве что-то есть
        'Массив для вывода. Тот же массив, что и промежуточный, только без пустых строк.
        Dim brr As Variant
        ReDim brr(1 To uu, 1 To UBound(arr, 2))
        For yy = 1 To UBound(brr, 1)
            For uu = 1 To UBound(brr, 2)
                brr(yy, uu) = arr(yy, uu)
            Next
        Next
        GetArrTabel = brr
    End If
End Function

Private Sub SortRange(rr As Range, xSort As Long)
'Процедура, сортирующая диапазон
     With rr.Parent.Sort
        .SortFields.Clear
        .SortFields.Add Key:=rr.Columns(xSort), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
        .SetRange rr: .Header = xlGuess: .MatchCase = False: .Orientation = xlTopToBottom: .SortMethod = xlPinYin
        .Apply
    End With
End Sub

Private Sub FillNumber(rr As Range)
'Заполнение номера, не табельного.
    If rr.Rows.Count = 1 Then
        rr.Cells(1, 1).Value = 1
    Else
        Dim arr As Variant
        arr = rr.Columns(1).Value
        Dim yy As Long
        For yy = 1 To UBound(arr, 1)
            arr(yy, 1) = yy
        Next
        rr.Columns(1).Value = arr
    End If
End Sub
 
МатросНаЗебре, а как мне теперь на лист "Тайминг" вывести в столбик уникальные ФИО, отсортированные по возрастанию, а в строчку - уникальные объекты, тоже отсортированные по возрастанию, в соответствии с выбором того же месяца, что и в табеле?
 
Код
'v4
Sub FillTiming()
    Dim arr As Variant
    arr = GetArrTiming(Sheets("Табель").Cells(1, 2).Value) 'Получаем массив для вывода
    If IsEmpty(arr) Then Exit Sub   'Если массива нет, выходим из макроса
    
    Dim yy As Long
    Dim brr As Variant
    
    With Sheets("Тайминг").Cells(1, 1).Resize(UBound(arr, 1), UBound(arr, 2))    'Диапазон на листе Табель
        .Value = arr    'Выводим массив
        SortRange .Cells, 2 'Сортируем
        .Columns(2).RemoveDuplicates Columns:=1, Header:=xlNo
        yy = .Cells(.Rows.Count, 2).End(xlUp).Row
        If yy = 1 Then yy = 2
        brr = .Cells(1, 2).Resize(yy)
        .ClearContents
    End With
    
    With Sheets("Тайминг").Cells(2, 1).Resize(UBound(arr, 1), UBound(arr, 2))    'Диапазон на листе Табель
        .Value = arr    'Выводим массив
        SortRange .Cells, 1 'Сортируем
        .Columns(1).RemoveDuplicates Columns:=1, Header:=xlNo
        .Columns(2).ClearContents
    End With
    
    With Sheets("Тайминг")
        .Cells(1, 2).Resize(1, UBound(brr, 1)) = Application.Transpose(brr)
    End With
End Sub

Sub FillTabel()
    Dim arr As Variant
    arr = GetArrTabel(Sheets("Табель").Cells(1, 2).Value) 'Получаем массив для вывода
    If IsEmpty(arr) Then Exit Sub   'Если массива нет, выходим из макроса
    
    With Sheets("Табель").Cells(4, 1).Resize(UBound(arr, 1), UBound(arr, 2))    'Диапазон на листе Табель
        .Value = arr    'Выводим массив
        SortRange .Cells, 2 'Сортируем
        FillNumber .Columns(1)  'Заполняем номер, первый столбец.
    End With
End Sub

Private Function GetArrTabel(monthTabel As Byte)
'Функция, формирующая массив табеля
    Dim tb As ListObject
    Set tb = Sheets("Для заполнения").ListObjects("Заполнение") 'Получаем умную таблицу на листе Для заполнения

    'Запоминаем в массивы столбцы умной таблицы
    Dim fio As Variant
    Dim dlj As Variant
    Dim cat As Variant
    Dim nom As Variant
    Dim dat As Variant
    
    With tb
        fio = .ListColumns("ФИО").Range
        dlj = .ListColumns("Должность").Range
        cat = .ListColumns("Категория персонала").Range
        nom = .ListColumns("Табельный номер").Range
        dat = .ListColumns("Дата").Range
    End With
    
    'Ключ для словаря для удаления дубликатов
    Dim sKey As String
    
    'Промежуточный массив с количеством строк, равным количеству строк в умной таблице
    Dim arr As Variant
    ReDim arr(1 To UBound(fio, 1), 1 To 5)
    
    'Cловарь для удаления дубликатов
    Dim dic As Object
    Set dic = CreateObject("Scripting.Dictionary")
    
    Dim flag As Boolean
    Dim yy As Long
    Dim uu As Long
    For yy = 2 To UBound(fio, 1)
        flag = False
        If IsDate(dat(yy, 1)) Then
            If Month(dat(yy, 1)) = monthTabel Then
                flag = True
            End If
        End If
        If flag Then
            'Ключ для удаления дубликатов. Учитываемые столбцы можно менять.
            sKey = Join(Array(nom(yy, 1), fio(yy, 1)), vbTab)
            If dic.Exists(sKey) Then flag = False
        End If
        If flag Then    'Если ключа ещё нет
            dic.Item(sKey) = 0  'Добавляем ключ в словарь
            uu = uu + 1         'Номер строки. Отличается от y на дубликаты.
            arr(uu, 1) = uu
            arr(uu, 2) = fio(yy, 1) 'ФИО
            arr(uu, 3) = dlj(yy, 1) 'Должность
            arr(uu, 4) = cat(yy, 1) 'Категория
            arr(uu, 5) = nom(yy, 1) 'Табельный номер
        End If
    Next
    
    If uu > 0 Then  'Если в промежуточном массиве что-то есть
        'Массив для вывода. Тот же массив, что и промежуточный, только без пустых строк.
        Dim brr As Variant
        ReDim brr(1 To uu, 1 To UBound(arr, 2))
        For yy = 1 To UBound(brr, 1)
            For uu = 1 To UBound(brr, 2)
                brr(yy, uu) = arr(yy, uu)
            Next
        Next
        GetArrTabel = brr
    End If
End Function

Private Function GetArrTiming(monthTabel As Byte) As Variant
'Функция, формирующая массив табеля
    Dim tb As ListObject
    Set tb = Sheets("Для заполнения").ListObjects("Заполнение") 'Получаем умную таблицу на листе Для заполнения

    'Запоминаем в массивы столбцы умной таблицы
    Dim fio As Variant
    Dim dlj As Variant
    Dim cat As Variant
    Dim nom As Variant
    Dim dat As Variant
    Dim obj As Variant
    
    
    With tb
        fio = .ListColumns("ФИО").Range
        dlj = .ListColumns("Должность").Range
        cat = .ListColumns("Категория персонала").Range
        nom = .ListColumns("Табельный номер").Range
        dat = .ListColumns("Дата").Range
        obj = .ListColumns("Объект").Range
    End With
    
    'Ключ для словаря для удаления дубликатов
    Dim sKey As String
    
    'Промежуточный массив с количеством строк, равным количеству строк в умной таблице
    Dim arr As Variant
    ReDim arr(1 To UBound(fio, 1), 1 To 2)
    
    'Cловарь для удаления дубликатов
    Dim dic As Object
    Set dic = CreateObject("Scripting.Dictionary")
    
    Dim flag As Boolean
    Dim yy As Long
    Dim uu As Long
    For yy = 2 To UBound(fio, 1)
        flag = False
        If IsDate(dat(yy, 1)) Then
            If Month(dat(yy, 1)) = monthTabel Then
                flag = True
            End If
        End If
        If flag Then
            'Ключ для удаления дубликатов. Учитываемые столбцы можно менять.
            sKey = Join(Array(nom(yy, 1), fio(yy, 1)), vbTab)
            If dic.Exists(sKey) Then flag = False
        End If
        If flag Then    'Если ключа ещё нет
            'dic.Item(sKey) = 0  'Добавляем ключ в словарь
            uu = uu + 1         'Номер строки. Отличается от y на дубликаты.
            arr(uu, 1) = fio(yy, 1) 'ФИО
            arr(uu, 2) = obj(yy, 1)
        End If
    Next
    
    If uu > 0 Then  'Если в промежуточном массиве что-то есть
        'Массив для вывода. Тот же массив, что и промежуточный, только без пустых строк.
        Dim brr As Variant
        ReDim brr(1 To uu, 1 To UBound(arr, 2))
        For yy = 1 To UBound(brr, 1)
            For uu = 1 To UBound(brr, 2)
                brr(yy, uu) = arr(yy, uu)
            Next
        Next
        GetArrTiming = brr
    End If
End Function

Private Sub SortRange(rr As Range, xSort As Long)
'Процедура, сортирующая диапазон
     With rr.Parent.Sort
        .SortFields.Clear
        .SortFields.Add Key:=rr.Columns(xSort), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
        .SetRange rr: .Header = xlGuess: .MatchCase = False: .Orientation = xlTopToBottom: .SortMethod = xlPinYin
        .Apply
    End With
End Sub

Private Sub FillNumber(rr As Range)
'Заполнение номера, не табельного.
    If rr.Rows.Count = 1 Then
        rr.Cells(1, 1).Value = 1
    Else
        Dim arr As Variant
        arr = rr.Columns(1).Value
        Dim yy As Long
        For yy = 1 To UBound(arr, 1)
            arr(yy, 1) = yy
        Next
        rr.Columns(1).Value = arr
    End If
End Sub
 
Огромное спасибо за помощь!
Страницы: 1
Наверх