Vba excel массив уникальных значений

Отбор уникальных значений из списка в VBA Excel с помощью объекта Collection. Выгрузка уникальных элементов в ListBox и ячейки рабочего листа. Скачать файл с примером кода.

Отбор уникальных значений из списка

При написании макросов для работы с данными в VBA Excel иногда возникает необходимость отбора уникальных значений из списка с повторяющимися элементами. Для этого можно воспользоваться следующим кодом:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

Sub ОтборУникальных()

‘Объявляем переменные

‘myRange — диапазон ячеек, заполненный исходным списком элементов

‘myCell — отдельная ячейка диапазона

‘myCollection — коллекция

‘myElement — элемент коллекции (должен быть типа «Variant»)

Dim myRange As Range, myCell As Range, myCollection As New Collection, _

myElement As Variant, i As Long

‘присваиваем переменной myRange диапазон ячеек с исходным списком элементов

Set myRange = Range(«A1:A20»)

‘заполняем новую коллекцию уникальными элементами

On Error Resume Next

For Each myCell In myRange

myCollection.Add CStr(myCell.Value), CStr(myCell.Value)

Next myCell

On Error GoTo 0

На этом отбор уникальных значений завершен. Коллекция заполнена уникальными элементами.

Добавление уникальных элементов в ListBox

Теперь можно добавить уникальные значения в ListBox, если перед этим создать форму UserForm1 и на нее добавить элемент управления ListBox1:

For Each myElement In myCollection

UserForm1.ListBox1.AddItem myElement

Next myElement

ListBox заполнен уникальными значениями из коллекции. Другие способы заполнения ListBox и ComboBox смотрите здесь.

Запись уникальных значений на рабочий лист

А так можно добавить уникальные элементы в ячейки столбца «В» активного рабочего листа:

For Each myElement In myCollection

i = i + 1

Cells(i, 2) = myElement

Next myElement

При необходимости сортируем полученный список в столбце «В»:

Range(Cells(1, 2), Cells(i, 2)).Sort Key1:=Range(«B1»), Order1:=xlAscending, _

Header:=xlGuess, OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom

А также можно отобразить количество найденных уникальных элементов, если, конечно, на форму UserForm1 добавлен элемент управления Label1:

UserForm1.Label1.Caption = «Уникальных элементов: « & myCollection.Count

‘отображаем форму

UserForm1.Show

End Sub

Если вам необходимо в ListBox или ComboBox загрузить отсортированный список, его элементы можно добавить с листа Excel после сортировки, в данном примере из диапазона Range(Cells(1, 2), Cells(i, 2)).

Обратите внимание, что в представленном коде VBA Excel для отбора уникальных значений из списка, выгрузки их в ListBox и записи на рабочий лист идет сплошная нумерация от Sub ОтборУникальных() и до End Sub.

Для наглядного ознакомления с работой представленного кода вы можете скачать демонстрационный файл.


Смотрите, как удалить повторяющиеся значения из диапазона ячеек в VBA Excel с помощью метода Range.RemoveDuplicates и отобрать уникальные значения из списка с помощью объекта Dictionary.


Функция UniqueValuesFromArray позволяет найти в указанном столбце двумерного массива все уникальные значения, и получить новый массив, содержащий все найденные уникальные значения.
Это может пригодиться, если надо, к примеру, заполнить ComboBox на форме возможными вариантами значений из базы данных:

Private Sub UserForm_Initialize()
    On Error Resume Next: arr = PriceRange.Value
    If Err Then MsgBox "Нет строк для обработки!", vbCritical, "Ошибка": End
 
    ' заполняем комбобокс уникальными значениями из 6-го столбца таблицы
    Me.ComboBox_Source.List = UniqueValuesFromArray(arr, 6)
End Sub

Код самой функции:

Function UniqueValuesFromArray(ByVal arr, ByVal col As Long) As Variant
    ' перебирает все значения в столбце Col двумерного массива arr
    ' в поисках уникальных значений. Возвращает двумерный вертикальный массив
    ' размерностью N * 1, содержащий уникальные значения из столбца col
    If Not IsArray(arr) Then MsgBox "Это не массив!", vbCritical: Exit Function
    If col > UBound(arr, 2) Then MsgBox "Нет такого столбца в массиве!", vbCritical: Exit Function
    If col < LBound(arr, 2) Then MsgBox "Нет такого столбца в массиве!", vbCritical: Exit Function
 
    On Error Resume Next: Dim coll As New Collection, txt$
    For i = LBound(arr) To UBound(arr)
        txt$ = Trim(arr(i, col)): coll.Add txt$, txt$
    Next i
    ReDim newarr(1 To coll.Count, 1 To 1)
    For i = 1 To coll.Count: newarr(i, 1) = coll(i): Next i
    UniqueValuesFromArray = newarr
End Function

Во вложении — пример использования этой функции в макросе (вывод уникальных записей в другой столбец листа), и пользовательская функция Уникальныедля использования в формулах листа Excel.

Макрос и дополнительная функция из файла во вложении:

Sub ВыборкаУникальных()
    ' берем диапазон ячеек из первого столбца активного листа
    Dim ПервыйСтолбец As Range: Set ПервыйСтолбец = Range([A1], Range("A" & Rows.Count).End(xlUp))
 
    ' выбираем из него уникальные значения
    МассивУникальных = UniqueValuesFromArray(ПервыйСтолбец.Value, 1)
 
    ' и заносим их в другой столбец, начиная с ячейки D1
    Range("D1").Resize(UBound(МассивУникальных)).Value = МассивУникальных
End Sub
' пользовательская функция - для использования в качестве формулы массива
Function Уникальные(ByVal ra As Range) As Variant
    ' перебирает все значения в диапазоне ra в поисках уникальных значений.
    ' Возвращает двумерный массив, содержащий уникальные значения из диапазона ra
    On Error Resume Next: Dim cell As Range, coll As New Collection, txt$
    For Each cell In ra.Cells
        txt$ = Trim(cell): If Len(txt$) Then coll.Add txt$, txt$
    Next cell
    ReDim newarr(1 To coll.Count, 1 To 1)
    For i = 1 To coll.Count: newarr(i, 1) = coll(i): Next i
    Уникальные = newarr
End Function
 

prostor

Пользователь

Сообщений: 69
Регистрация: 04.06.2017

#1

10.06.2017 12:32:54

Приветствую,

Не получается доработать код макроса под себя, может кто-то из опытных поможет в этом деле!?))

Исходная задача такова: На входе уже есть массив данных из которого надо выбрать уникальные значения.

В процессе, нашел на просторах инета нужный код, но (для меня) проблема в том, что нужная функция принимает в параметре переменную-диапазон, а мне надо передавать i-й элемент массива. Вот сам код макросса:

Код
'Массив  FirstArr() определяется выше по коду и содержит 100 элементов

 Sub UnikArr()
 
    Dim MyData As Range: Set MyData = Range([A1], Range("A" & Rows.Count).End(xlUp))
    Dim ArrRng() As Variant

    ArrRng = UniqueValuesFromArray(MyData.Value, 1) 'Тут нужен цикл For и вместо MyData.Value, 1 подставить FirstArr(0, i)

    Range("B1").Resize(UBound(ArrRng)).Value = ArrRng


End Sub

Function UniqueValuesFromArray(ByVal ArrTmp, ByVal col As Long) As Variant
Dim i As Long


    If Not IsArray(ArrTmp) Then MsgBox "Это не массив!", vbCritical: Exit Function
    If col > UBound(ArrTmp, 2) Then MsgBox "Нет такого столбца в массиве!", vbCritical: Exit Function
    If col < LBound(ArrTmp, 2) Then MsgBox "Нет такого столбца в массиве!", vbCritical: Exit Function

    On Error Resume Next: Dim coll As New Collection, txt$
    
    For i = LBound(ArrTmp) To UBound(ArrTmp)
        txt$ = Trim(ArrTmp(i, col)): coll.Add txt$, txt$
    Next i
    ReDim MyArr(1 To coll.Count, 1 To 1)
    For i = 1 To coll.Count: MyArr(i, 1) = coll(i): Next i
    UniqueValuesFromArray = MyArr
End Function

Заранее, благодарю

Прикрепленные файлы

  • Arr_Unique.xlsm (16.59 КБ)

Изменено: prostor10.06.2017 13:19:01

 

JayBhagavan

Пользователь

Сообщений: 11833
Регистрация: 17.01.2014

ПОЛ: МУЖСКОЙ | Win10x64, MSO2019x64

#2

10.06.2017 12:40:37

Цитата
2.3. Приложите файл(ы) с примером (общим весом не более 100 Кб) в реальной структуре и форматах данных того, что есть сейчас и того, что хотелось бы на выходе.

<#0>
Формула массива (ФМ) вводится Ctrl+Shift+Enter
Memento mori

 

SuperCat

Пользователь

Сообщений: 2737
Регистрация: 21.12.2012

#3

10.06.2017 12:45:49

Код
 Sub UnikArr()
  
    Dim MyData As Range
    Dim ArrRng As Variant
    
    Set MyData = Range([A1], Range("A" & Rows.Count).End(xlUp))
    With Sheets.Add
        With .Range("A1").Resize(MyData.Count)
            .Value = MyData.Value
            .RemoveDuplicates Array(1)
        End With
        ArrRng = .Range("A1").CurrentRegion
        Application.DisplayAlerts = False
        .Delete
        Application.DisplayAlerts = True
    End With
     
    Range("B1").Resize(UBound(ArrRng)).Value = ArrRng
 
End Sub

There is no knowledge that is not power

 

Все_просто

Пользователь

Сообщений: 1042
Регистрация: 10.06.2013

#4

10.06.2017 13:19:47

Код
Sub start()
Dim arr
arr = Selection
Dim dic As Object
Set dic = CreateObject("Scripting.Dictionary")
Dim el
For Each el In arr
    If Not dic.exists(el) Then dic.Add el, el
Next
arr = dic.keys()
End Sub

Вместо arr может быть любой нужный вам массив.

С уважением,
Федор/Все_просто

 

prostor

Пользователь

Сообщений: 69
Регистрация: 04.06.2017

JayBhagavan,Сорри, добавил файл макроса с примечаниями….

 

prostor

Пользователь

Сообщений: 69
Регистрация: 04.06.2017

SuperCat,Надо вообще отвязаться от диапазона MyData As Range. Добавил в первый пост файл с комментариями.

 

prostor

Пользователь

Сообщений: 69
Регистрация: 04.06.2017

Все_просто,спасибо. Все получилось!

Правильно ли я понимаю, что: CreateObject(«Scripting.Dictionary») используется только для подключения метода .exists — который удаляет дубли?
У массивов такого метода нет? Верно?

 

SuperCat

Пользователь

Сообщений: 2737
Регистрация: 21.12.2012

#8

10.06.2017 20:18:26

Код
For Each el In arr
    If Not dic.exists(el) Then dic.Add el, el
Next

можно упростить до:

Код
For Each el In arr
    dic(el) = el
Next

There is no knowledge that is not power

 

Юрий М

Модератор

Сообщений: 60575
Регистрация: 14.09.2012

Контакты см. в профиле

Отобрать уникальные из массива можно и при помощи коллекции.

 

prostor

Пользователь

Сообщений: 69
Регистрация: 04.06.2017

Юрий М,у коллекции есть такой метод ? или это как то по-особому делается? ибо в справке не нашел такого

https://msdn.microsoft.com/ru-ru/library/yb7y698k(v=vs.90).aspx

Киньте пример, если не сложно.

 

Юрий М

Модератор

Сообщений: 60575
Регистрация: 14.09.2012

Контакты см. в профиле

#11

10.06.2017 20:32:48

Код
Sub TestUniq()
Dim Uniq As New Collection, Arr(), x
    Arr = Selection.Value
    For Each x In Arr
        On Error Resume Next
        Uniq.Add x, CStr(x)
    Next
End Sub
 

prostor

Пользователь

Сообщений: 69
Регистрация: 04.06.2017

Детально прочитал справку по всем элементам из вашего примера, не понял как именно отбираются уникальные значения:((

Изменено: prostor10.06.2017 20:46:37

 

Юрий М

Модератор

Сообщений: 60575
Регистрация: 14.09.2012

Контакты см. в профиле

Коллекция просто не позволит добавить в себя уже существующий элемент — это вызвало бы ошибку. Но строка On Error Resume Next приходит на помощь: разрешает продолжить выполнение программы )

 

prostor

Пользователь

Сообщений: 69
Регистрация: 04.06.2017

Юрий М,Это офигенно круто!:)) Засяду ка и изучу этот объект!

Я так понимаю, что элементы коллекции (например строки), можно так же обрабатывать (регулярками, например) как и элементы массива?

 

Юрий М

Модератор

Сообщений: 60575
Регистрация: 14.09.2012

Контакты см. в профиле

Есть строка — нет разницы, чем её обрабатывать ))
А с коллекцией есть один немаловажный момент: раньше я всегда перебирал элементы готовой коллекции обычным циклом For-Next. Заметил, что при большом количестве данных скорость катастрофически падает с каждым элементом. По совету ZVI стал использовать For Each-Next — тормоза пропали!  

 

prostor

Пользователь

Сообщений: 69
Регистрация: 04.06.2017

Юрий М,что посоветуете почитать/посмотреть по Коллекциям? А то я штудирую msdn, но там как-то ограниченная инфа очень…

 

SuperCat

Пользователь

Сообщений: 2737
Регистрация: 21.12.2012

Котяра, тогда On Error Resume Next лучше в начала процедуры поставить

There is no knowledge that is not power

 

prostor

Пользователь

Сообщений: 69
Регистрация: 04.06.2017

#18

10.06.2017 21:08:56

Цитата
SuperCat написал:
Котяра, тогда On Error Resume Next лучше в начала процедуры поставить

За пределами цикла?

 

Юрий М

Модератор

Сообщений: 60575
Регистрация: 14.09.2012

Контакты см. в профиле

Ничего не могу посоветовать (
И Вы показали ссылку на справку по VB — это ведь не совсем VBA )

 

prostor

Пользователь

Сообщений: 69
Регистрация: 04.06.2017

#20

10.06.2017 21:13:54

Цитата
Юрий М написал:
И Вы показали ссылку на справку по VB — это ведь не совсем VBA )

Это да. Так нагуглилось просто:))

Благодарю за помощь. Пошел учится…

 

По поводу словарей. Это, можно сказать, основной мой инструмент в работе. Чуть ли не каждый второй мой макрос для заказчиков использует именно словари/многократно вложенные словари (привычка из Python). Для меня словари намного легче громоздких отвлекающих конструкций с «редим презервами». Словари тем более удобны, что методы keys() и items() дают на выходе одномерные массивы, которые потом очень легко выводить в диапазоны. С помощью вложенных словарей очень просто реализуется программируемая сводная таблица.
Коллекции — другая опера. По возможности стараюсь использовать как можно реже (хотя иногда и лень). Для некоторых целей не совсем подходит. Только write-once/read-only (название — мое изобретение) чего стоит (в словарях можно менять уже внесенные значения, хотя для вложенных словарей нужно добавлять дополнительную переменную). Это свойство коллекций очень ограничивает.

До сих пор, словари меня не подводили. Надеюсь и не будут.

С уважением,
Федор/Все_просто

 

AndreTM

Пользователь

Сообщений: 454
Регистрация: 02.05.2013

Да, словари (объект Scripting.Dictionary из WSH) — это, по идее, обязательная тема изучения, для тех, кому требуется массовая обработка данных. Простые коллекции VBA всё же менее функциональны, а для многих задач функционал запросов (сводные/Power*/sql) излишний.

 

Юрий М

Модератор

Сообщений: 60575
Регистрация: 14.09.2012

Контакты см. в профиле

Коллеги, тема про извлечение уникальных значений из массива, а не про словари v/s коллекции ))

 

SuperCat

Пользователь

Сообщений: 2737
Регистрация: 21.12.2012

#24

10.06.2017 22:57:17

Цитата
prostor написал:
За пределами цикла?

Да

There is no knowledge that is not power

 

prostor

Пользователь

Сообщений: 69
Регистрация: 04.06.2017

Вопрос, конечно уже выходит за рамки этой темы, но все же (не могу нагуглить) — каково исходное назначение словарей и коллекций? Ведь не зря они существуют как самостоятельные инструменты, и с разными «свойствами», методами….

 

Sanja

Пользователь

Сообщений: 14838
Регистрация: 10.01.2013

#26

11.06.2017 10:33:56

Про словари

Согласие есть продукт при полном непротивлении сторон.

Хитрости »

1 Май 2011              532036 просмотров


Как получить список уникальных(не повторяющихся) значений?

Представим себе большой список различных наименований, ФИО, табельных номеров и т.п. А необходимо из этого списка оставить список все тех же наименований, но чтобы они не повторялись — т.е. удалить из этого списка все дублирующие записи. Как это иначе называют: создать список уникальных элементов, список неповторяющихся, без дубликатов. Для этого существует несколько способов: встроенными средствами Excel, встроенными формулами и, наконец, при помощи кода Visual Basic for Application(VBA) и сводных таблиц. В этой статье рассмотрим каждый из вариантов.

  • При помощи встроенных возможностей Excel 2007 и выше
  • При помощи Расширенного фильтра
  • При помощи формул
  • При помощи кодов Visual Basic for Application(VBA) — макросы, включая универсальный код выборки из произвольного диапазона
  • При помощи сводных таблиц

при помощи встроенных возможностей Excel 2007 и выше

В Excel 2007 и 2010 это сделать проще простого — есть специальная команда, которая так и называется — Удалить дубликаты (Remove Duplicates). Расположена она на вкладке Данные (Data) подраздел Работа с данными (Data tools)

Как использовать данную команду. Выделяете столбец(или несколько) с теми данными, в которых надо удалить дублирующие записи. Идете на вкладку Данные (Data)Удалить дубликаты (Remove Duplicates).

Если выделить один столбец, но рядом с ним будут еще столбцы с данными(или хотя бы один столбец), то Excel предложит выбрать: расширить диапазон выборки этим столбцом или оставить выделение как есть и удалить данные только в выделенном диапазоне. Важно помнить, что если не расширить диапазон, то данные будут изменены лишь в одном столбце, а данные в прилегающем столбце останутся без малейших изменений.

Появится окно с параметрами удаления дубликатов

Ставите галочки напротив тех столбцов, дубликаты в которых надо удалить и жмете Ок. Если в выделенном диапазоне так же расположены заголовки данных, то лучше поставить флаг Мои данные содержат заголовки, чтобы случайно не удалить данные в таблице(если они вдруг полностью совпадают со значением в заголовке).


Способ 1: Расширенный фильтр

В случае с Excel 2003 все посложнее. Там нет такого инструмента, как Удалить дубликаты. Но зато есть такой замечательный инструмент, как Расширенный фильтр. В 2003 этот инструмент можно найти в ДанныеФильтрРасширенный фильтр. Прелесть этого метода в том, с его помощью можно не портить исходные данные, а создать список в другом диапазоне.

В 2007-2010 Excel, он тоже есть, но немного запрятан. Расположен на вкладке Данные (Data), группа Сортировка и фильтр (Sort & Filter)Дополнительно (Advanced)

Как его использовать: запускаем указанный инструмент — появляется диалоговое окно:

  • Обработка: Выбираем Скопировать результат в другое место (Copy to another location).
  • Исходный диапазон (List range): Выбираем диапазон с данными(в нашем случае это А1:А51).
  • Диапазон критериев (Criteria range): в данном случае оставляем пустым.
  • Поместить результат в диапазон (Copy to): указываем первую ячейку для вывода данных — любую пустую(на картинке — E2).
  • Ставим галочку Только уникальные записи (Unique records only).
  • Жмем Ок.

Примечание: если вы хотите поместить результат на другой лист, то просто так указать другой лист не получится. Вы сможете указать ячейку на другом листе, но…Увы и ах…Excel выдаст сообщение, что не может скопировать данные на другие листы. Но и это можно обойти, причем довольно просто. Надо всего лишь запустить Расширенный фильтр с того листа, на который хотим поместить результат. А в качестве исходных данных выбираем данные с любого листа — это дозволено.

Так же можно не выносить результат в другие ячейки, а отфильтровать данные на месте. Данные от этого никак не пострадают — это будет обычная фильтрация данных.

Для этого надо просто в пункте Обработка выбрать Фильтровать список на месте (Filter the list, in-place).


Способ 2: Формулы

Этот способ сложнее в понимании для неопытных пользователей, но зато он создает список уникальных значений, не изменяя при этом исходные данные. Ну и он более динамичен: если изменить данные в исходной таблице, то изменится и результат. Иногда это бывает полезно. Попытаюсь объяснить на пальцах что и к чему: допустим, список с данными у Вас расположен в столбце

А

(

А1:А51

, где

А1

— заголовок). Выводить список мы будем в столбец

С

, начиная с ячейки

С2

. Формула в

C2

будет следующая:

{=ИНДЕКС($A$2:$A$51;НАИМЕНЬШИЙ(ЕСЛИ(СЧЁТЕСЛИ($C$1:C1;$A$2:$A$51)=0;СТРОКА($A$1:$A$50));1))}
{=INDEX($A$2:$A$51;SMALL(IF(COUNTIF($C$1:C1;$A$2:$A$51)=0;ROW($A$1:$A$50));1))}
Детальный разбор работы данной формулы приведен в статье: Как просмотреть этапы вычисления формул

Надо отметить, что эта формула является формулой массива. Об этом могут сказать фигурные скобки, в которые заключена данная формула. А вводится такая формула в ячейку сочетанием клавиш —

Ctrl

+

Shift

+

Enter

(при этом сами скобки вводить не надо — они появятся сами после ввода формулы тремя клавишами

Ctrl

+

Shift

+

Enter

). После того, как мы ввели эту формулу в

C2

мы её должны скопировать и вставить в несколько строк так, чтобы точно отобразить все уникальные элементы. Как только формула в нижних ячейках вернет

#ЧИСЛО!(#NUM!)

— это значит все элементы отображены и ниже протягивать формулу нет смысла. Чтобы ошибку избежать и сделать формулу более универсальной(не протягивая каждый раз до появления ошибки) можно использовать нехитрую проверку:

для Excel 2007 и выше:
{=ЕСЛИОШИБКА(ИНДЕКС($A$2:$A$51;НАИМЕНЬШИЙ(ЕСЛИ(СЧЁТЕСЛИ($C$1:C1;$A$2:$A$51)=0;СТРОКА($A$1:$A$50));1));»»)}
{=IFERROR(INDEX($A$2:$A$51;SMALL(IF(COUNTIF($C$1:C1;$A$2:$A$51)=0;ROW($A$1:$A$50));1));»»)}
для Excel 2003:
{=ЕСЛИ(ЕОШ(НАИМЕНЬШИЙ(ЕСЛИ(СЧЁТЕСЛИ($C$1:C1;$A$2:$A$51)=0;СТРОКА($A$1:$A$50));1));»»;ИНДЕКС($A$2:$A$51;НАИМЕНЬШИЙ(ЕСЛИ(СЧЁТЕСЛИ($C$1:C1;$A$2:$A$51)=0;СТРОКА($A$1:$A$50));1)))}
{=IF(ISERR(SMALL(IF(COUNTIF($C$1:C1;$A$2:$A$51)=0;ROW($A$1:$A$50));1));»»;INDEX($A$2:$A$51;SMALL(IF(COUNTIF($C$1:C1;$A$2:$A$51)=0;ROW($A$1:$A$50));1)))}

Тогда вместо ошибки 

#ЧИСЛО!(#NUM!)

у вас будут пустые ячейки(не совсем пустые, конечно — с формулами :-)).
Чуть подробнее про отличия и нюансы формул ЕСЛИОШИБКА и ЕСЛИ(ЕОШ можно прочесть в этой статье: Как в ячейке с формулой вместо ошибки показать 0


Для пользователей Excel 2021 выше, а так же пользователей Excel 365(с активной подпиской) — использовать формулы для извлечения уникальных элементов проще простого. В этих версиях появилась функция

УНИК(UNIQUE)

, которая как раз получает список уникальных значений на основании переданного диапазона:

=УНИК($A$2:$A$51)
=UNIQUE($A$2:$A$51)

Что самое важное в данном случае — это функция динамического массива и вводить её надо только в одну ячейку C2, а результат она поместит сама в нужное количество ячеек.


Способ 3: код VBA

Данный подход потребует разрешения макросов и базовых знаний о работе с ними. Если не уверены в своих знаниях для начала рекомендую прочитать эти статьи:

  • Что такое макрос и где его искать? к статье приложен видеоурок
  • Что такое модуль? Какие бывают модули? потребуется, чтобы понять куда вставлять приведенные ниже коды

Оба приведенных ниже кода следует помещать в стандартный модуль. Макросы должны быть разрешены.

Исходные данные оставим в том же порядке — список с данными расположен в столбце «А«(А1:А51, где А1 — заголовок). Только выводить список мы будем не в столбец С, а в столбец Е, начиная с ячейки Е2:

Sub Extract_Unique()
    Dim vItem, avArr, li As Long
    ReDim avArr(1 To Rows.Count, 1 To 1)
    With New Collection
        On Error Resume Next
        For Each vItem In Range("A2", Cells(Rows.Count, 1).End(xlUp)).Value
            'Cells(Rows.Count, 1).End(xlUp) – определяет последнюю заполненную ячейку в столбце А
            .Add vItem, CStr(vItem)
            If Err = 0 Then
                li = li + 1: avArr(li, 1) = vItem
            Else: Err.Clear
            End If
        Next
    End With
    If li Then [E2].Resize(li).Value = avArr
End Sub

С помощью данного кода можно извлечь уникальные не только из одного столбца, но и из любого диапазона столбцов и строк. Если вместо строки
Range(«A2», Cells(Rows.Count, 1).End(xlUp)).Value
указать Selection.Value, то результатом работы кода будет список уникальных элементов из выделенного на активном листе диапазона. Только тогда неплохо бы и ячейку вывода значений изменить — вместо [E2] поставить ту, в которой данных нет.
Так же можно указать конкретный диапазон:

Или другой столбец:

Range("C2", Cells(Rows.Count, 3).End(xlUp)).Value

здесь отдельно стоит обратить внимание то, что в данном случае помимо изменения А2 на С2 изменилась и цифра 1 на 3. Это указание на номер столбца, в котором необходимо определить последнюю заполненную ячейку, чтобы код не просматривал лишние ячейки. Подробнее про это можно прочитать в статье: Как определить последнюю ячейку на листе через VBA?

Универсальный код выбора уникальных значений
Код ниже можно применять для любых диапазонов. Достаточно запустить его, указать диапазон со значениями для отбора только неповторяющихся(допускается выделение более одного столбца) и ячейку для вывода результата. Указанные ячейки будут просмотрены, из них будут отобраны только уникальные значения(пустые ячейки при этом пропускаются) и результирующий список будет записан, начиная с указанной ячейки.

Sub Extract_Unique()
    Dim x, avArr, li As Long
    Dim avVals
    Dim rVals As Range, rResultCell As Range
 
    On Error Resume Next
    'запрашиваем адрес ячеек для выбора уникальных значений
    Set rVals = Application.InputBox("Укажите диапазон ячеек для выборки уникальных значений", "Запрос данных", "A2:A51", Type:=8)
    If rVals Is Nothing Then 'если нажата кнопка Отмена
        Exit Sub
    End If
    'если указана только одна ячейка - нет смысла выбирать
    If rVals.Count = 1 Then
        MsgBox "Для отбора уникальных значений требуется указать более одной ячейки", vbInformation, "www.excel-vba.ru"
        Exit Sub
    End If
    'отсекаем пустые строки и столбцы вне рабочего диапазона
    Set rVals = Intersect(rVals, rVals.Parent.UsedRange)
    'если указаны только пустые ячейки вне рабочего диапазона
    If rVals Is Nothing Then
        MsgBox "Недостаточно данных для выбора значений", vbInformation, "www.excel-vba.ru"
        Exit Sub
    End If
    avVals = rVals.Value
    'запрашиваем ячейку для вывода результата
    Set rResultCell = Application.InputBox("Укажите ячейку для вставки отобранных уникальных значений", "Запрос данных", "E2", Type:=8)
    If rResultCell Is Nothing Then 'если нажата кнопка Отмена
        Exit Sub
    End If
    'определяем максимально возможную размерность массива для результата
    ReDim avArr(1 To Rows.Count, 1 To 1)
    'при помощи объекта Коллекции(Collection)
    'отбираем только уникальные записи,
    'т.к. Коллекции не могут содержать повторяющиеся значения
    With New Collection
        On Error Resume Next
        For Each x In avVals
            If Len(CStr(x)) Then 'пропускаем пустые ячейки
                .Add x, CStr(x) 'если добавляемый элемент уже есть в Коллекции - возникнет ошибка
                'если же ошибки нет - такое значение еще не внесено,
                'добавляем в результирующий массив
                If Err = 0 Then
                    li = li + 1
                    avArr(li, 1) = x
                Else
                    'обязательно очищаем объект Ошибки
                    Err.Clear
                End If
            End If
        Next
    End With
    'записываем результат на лист, начиная с указанной ячейки
    If li Then rResultCell.Cells(1, 1).Resize(li).Value = avArr
End Sub

Способ 4: Сводные таблицы

Несколько нестандартный способ извлечения уникальных значений.

  • Выделяем один или несколько столбцов в таблице, переходим на вкладку Вставка(Insert) -группа Таблица(Table)Сводная таблица(PivotTable)
  • В диалоговом окне Создание сводной таблицы(Create PivotTable) проверяем правильность выделения диапазона данных (или установить новый источник данных)
  • указываем место размещения Сводной таблицы:
    • На новый лист (New Worksheet)
    • На существующий лист (Existing Worksheet)
  • подтверждаем создание нажатием кнопки OK

Т.к. сводные таблицы при обработке данных, которые помещаются в область строк или столбцов, отбирают из них только уникальные значения для последующего анализа, то от нас ровным счетом ничего не требуется, кроме как создать сводную таблицу и поместить в область строк или столбцов данные нужного столбца.
На примере приложенного к статье файла я:

  • выделил диапазон A1:B51 на листе Извлечение по критерию
  • вызвал меню вставки сводной таблицы: вкладка Вставка(Insert) -группа Таблица(Table)Сводная таблица(PivotTable)
    выбрал вставить на новый лист(New Worksheet)
  • назвал этот лист Уникальные сводной таблицей
  • поле Данные поместил в область строк
  • поле ФИО в область фильтра. Почему? Чтобы удобно было выбирать одно или несколько ФИО и в сводной отображался бы список уникальных месяцев только для выбранных фамилий
    Отбор уникальных сводной таблицей

В чем неудобство работы со сводными в данном случае: при изменении в исходных данных сводную таблицу придется обновлять вручную: Выделить любую ячейку сводной таблицы -Правая кнопка мыши —Обновить(Refresh) или вкладка Данные(Data)Обновить все(Refresh all)Обновить(Refresh). А если исходные данные пополняются динамически и того хуже — надо будет заново указывать диапазон исходных данных. И еще один минус — данные внутри сводной таблицы нельзя менять. Поэтому если с полученным списком необходимо будет работать в дальнейшем, то после создания нужного списка при помощи сводной его надо скопировать и вставить на нужный лист.

Чтобы лучше понимать все действия и научиться обращаться со сводными таблицами настоятельно рекомендую ознакомиться со статьей Общие сведения о сводных таблицах — к ней приложен видеоурок, в котором я наглядно демонстрирую простоту и удобство работы с основными возможностями сводных таблиц.


В приложенном примере помимо описанных приемов, записана чуть более сложная вариация извлечения уникальных элементов формулой и кодом, а именно: извлечение уникальных элементов по критерию. О чем речь: если в одном столбце фамилии, а во втором(В) некие данные(в файле это месяцы) и требуется извлечь уникальные значения столбца В только для выбранной фамилии. Примеры подобных извлечений уникальных расположены на листе Извлечение по критерию.

Скачать пример:

  Tips_All_ExtractUnique.xls (108,0 KiB, 18 430 скачиваний)

Также см.:
Работа с дубликатами
Как подсчитать количество повторений
Общие сведения о сводных таблицах


Статья помогла? Поделись ссылкой с друзьями!

  Плейлист   Видеоуроки


Поиск по меткам



Access
apple watch
Multex
Power Query и Power BI
VBA управление кодами
Бесплатные надстройки
Дата и время
Записки
ИП
Надстройки
Печать
Политика Конфиденциальности
Почта
Программы
Работа с приложениями
Разработка приложений
Росстат
Тренинги и вебинары
Финансовые
Форматирование
Функции Excel
акции MulTEx
ссылки
статистика

The old school method was my favourite option. Thank you. And it was indeed fast. But I didn’t use redim. Here though is my real world example where I accumulate values for each unique «key» found in a column and move it into a array (say for an employee and values are hours worked per day). Then I put each key with its final values into a totals area on the active sheet. I’ve commented extensively for anyone who wants painful detail on what is happening here. Limited error checking is done by this code.

Sub GetActualTotals()
'
' GetActualTotals Macro
'
' This macro accumulates values for each unique employee from the active
' spreadsheet.
'
' History
' October 2016 - Version 1
'
' Invocation
' I created a button labeled "Get Totals" on the Active Sheet that invokes
' this macro.
'
Dim ResourceName As String
Dim TotalHours As Double
Dim TotalPercent As Double
Dim IsUnique As Boolean
Dim FirstRow, LastRow, LastColumn, LastResource, nUnique As Long
Dim CurResource, CurrentRow, i, j As Integer
Dim Resource(1000, 2) As Variant
Dim Rng, r As Range
'
' INITIALIZATIONS
'
' These are index numbers for the Resource array
'
Const RName = 0
Const TotHours = 1
Const TotPercent = 2
'
' Set the maximum number of resources we'll
' process.
'
Const ResourceLimit = 1000
'
' We are counting on there being no unintended data
' in the spreadsheet.
'
' It won't matter if the cells are empty though. It just
' may take longer to run the macro.
' But if there is data where this macro does not expect it,
' assume unpredictable results.
'
' There are some hardcoded values used.
' This macro just happens to expect the names to be in Column C (or 3).
'
' Get the last row in the spreadsheet:
'
LastRow = Cells.Find(What:="*", _
                After:=Range("C1"), _
                LookAt:=xlPart, _
                LookIn:=xlFormulas, _
                SearchOrder:=xlByRows, _
                SearchDirection:=xlPrevious, _
                MatchCase:=False).Row
'
'  Furthermore, this macro banks on the first actual name to be in C6.
'  so if the last row is row 65, the range we'll work with 
'  will evaluate to "C6:C65"
'
FirstRow = 6
Rng = "C" & FirstRow & ":C" & LastRow
Set r = Range(Rng)
'
' Initialize the resource array to be empty (even though we don't really
' need to but I'm old school).  
'
For CurResource = 0 To ResourceLimit
    Resource(CurResource, RName) = ""
    Resource(CurResource, TotHours) = 0
    Resource(CurResource, TotPercent) = 0
Next CurResource
'
' Start the resource counter at 0.  The counter will represent the number of
' unique entries. 
'
 nUnique = 0
'
' LET'S GO
'
' Loop from the first relative row and the last relative row
' to process all the cells in the spreadsheet we are interested in
'
For i = 1 To LastRow - FirstRow
'
' Loop here for all unique entries. For any
' new unique entry, that array element will be
' initialized in the second if statement.
'
    IsUnique = True
    For j = 1 To nUnique
'
' If the current row element has a resource name and is already
' in the resource array, then accumulate the totals for that
' Resource Name. We then have to set IsUnique to false and
' exit the for loop to make sure we don't populate
' a new array element in the next if statement.
'
        If r.Cells(i, 1).Value = Resource(j, RName) Then
            IsUnique = False
            Resource(j, TotHours) = Resource(j, TotHours) + _
            r.Cells(i, 4).Value
            Resource(j, TotPercent) = Resource(j, TotPercent) + _
            r.Cells(i,5).Value
            Exit For
        End If
     Next j
'
' If the resource name is unique then copy the initial
' values we find into the next resource array element.
' I ignore any null cells.   (If the cell has a blank you might
' want to add a Trim to the cell).   Not much error checking for 
' the numerical values either.
'
    If ((IsUnique) And (r.Cells(i, 1).Value <> "")) Then
        nUnique = nUnique + 1
        Resource(nUnique, RName) = r.Cells(i, 1).Value
        Resource(nUnique, TotHours) = Resource(nUnique, TotHours) + _ 
        r.Cells(i, 4).Value
        Resource(nUnique, TotPercent) = Resource(nUnique, TotPercent) + _
        r.Cells(i, 5).Value
    End If                  
Next i
'
' Done processing all rows
'
' (For readability) Set the last resource counter to the last value of
' nUnique.
' Set the current row to the first relative row in the range (r=the range).
'
LastResource = nUnique
CurrentRow = 1
'
' Populate the destination cells with the accumulated values for
' each unique resource name.
'
For CurResource = 1 To LastResource
    r.Cells(CurrentRow, 7).Value = Resource(CurResource, RName)
    r.Cells(CurrentRow, 8).Value = Resource(CurResource, TotHours)
    r.Cells(CurrentRow, 9).Value = Resource(CurResource, TotPercent)
    CurrentRow = CurrentRow + 1
Next CurResource

End Sub

Like this post? Please share to your friends:
  • Vba excel макрос для поиска
  • Vba excel массив с разными типами
  • Vba excel массив с единицы
  • Vba excel массив поиск
  • Vba excel массив количество элементов