2014-10-15 14 views
2

Я пытаюсь создать динамический словарь, содержащий динамические массивы.Словарь VBA с динамическими массивами

Пример строки из таблицы:

Facility Name|Contact Name|Contact Role 

Отношения между объектами и контактов M2M. Я хотел бы, чтобы воссоздать лист, который выглядит следующим образом:

Contact Name| Facility1 - role, Facility2 - role 

То, что я хотел бы сделать, это создать словарь имен с уникальными именами, выступающими в качестве ключей

New Dictionary Names(name) 

Значения для имен (имя) будет массивом всех номеров строк, где это имя появляется. Например, скажем, «Джо Роуз» появляется в строках 3, 7 и 9:

names("Joe Rose") = [3,7,9] 

Я знаю, как я мог бы сделать это в JS, Python, PHP, но VBA сводит меня с ума!

Вот что я бы получил до сих пор:

Dim names As Dictionary 
Set names = New Dictionary 

Dim name 

For i=1 To WorkSheets("Sheet1").Rows.Count 
    name = WorkSheets("Sheet1").Cells(i,2) 
    If Not names(name) Then 
    names(name) = i 
    Else 
    'help! 
    'names(name)) push new i, maybe something with redim preserve? 
    End If 
Next i 

Даже просто указал мне в какой-то статье, что я мог ссылаться бы здорово! VBA так расстраивает, исходя из фона PHP!

Спасибо

ответ

4

Это немного сложнее, так как вы должны вытащить массив из словаря, чтобы работать с ним, а затем положить его обратно:

Sub Tester() 

    Dim names As Dictionary 
    Set names = New Dictionary 

    Dim name, tmp, ub, i, k 

    For i = 1 To Worksheets("Sheet1").UsedRange.Rows.Count 

     name = Trim(Worksheets("Sheet1").Cells(i, 2).Value) 

     If Len(name) > 0 Then 
      If Not names.Exists(name) Then 
       names(name) = Array(i) 
      Else 
       tmp = names(name) 
       ub = UBound(tmp) + 1 
       ReDim Preserve tmp(0 To ub) 
       tmp(ub) = i 
       names(name) = tmp 
      End If 
     End If 
    Next i 

    For Each k In names.Keys 
     Debug.Print k, Join(names(k), ",") 
    Next k 


End Sub 
+0

Это тот подход, который я хотел принять, но я просто не привык переопределять размер массивов (я просто привык к «pushing» значение в!) – ZAR

+0

Yep - no 'push()' в VBA ;-( –

2

Давайте сделаем это. Сначала создайте значение словаря как строку с разделителями-запятыми. Затем, если вам нужно/нужно, вы можете использовать функцию SPLIT, чтобы преобразовать ее в массив.

Dim names As Dictionary 
Set names = New Dictionary 

Dim name 

For i = 1 To WorkSheets("Sheet1").Rows.Count 
    name = WorkSheets("Sheet1").Cells(i,2) 

    If names.Exists(name) Then 
     names(name) = names(name) & "," & i 
    Else 
     names(name) = i 
    Next 

Next i 

For each name in names 
    names(name) = Split(name, ",") 
Next 
+0

Это хорошая идея - сделать ее легко разделяемой способностью. Функция Split вернет строку как массив, как я только что прочитал. Спасибо, Дэвид! – ZAR

+1

Хотя ответ Тима в конечном счете ответил на этот вопрос, я подумал, что ваша идея сначала поставить его в строку, а затем расщеплять позже, была умной. К сожалению, поскольку я работаю с более чем 200k строк, дополнительный цикл для разделения строк приводит к тому, что excel может висеть! Я немного удивлен этим, потому что его решение требует создания нового массива и переназначения довольно часто среднего цикла, но решение на удивление быстро. hmmm ... – ZAR

+0

Я не тестировал ни на какие данные, почти такие большие. Тем не менее это нечетно ... потому что первый цикл всегда будет повторяться один раз для каждой строки (и особенно в случае ответа Тима я всегда понимал, что 'ReDim Preserve' является дорогостоящей операцией и, как правило, следует избегать * внутри * Петли, если это возможно), тогда как второй цикл повторяется только для каждого «ключа» в словаре, который должен быть меньше. Возможно, функция «Сплит» является дорогостоящей с точки зрения использования памяти, хотя ... –

1

Try, чтобы избежать использования [таблица] .rows.count при зацикливании его значение составляет более 1 млн. для excel 2010.

Public Sub test() 
    Dim names As Dictionary 
    Dim name 
    Dim cell As Object 

    'finds last row in column 2 
    lastRow = Worksheets("Sheet1").Cells(Rows.Count, 2).End(xlUp).Row 
    Set names = New Dictionary 

    For Row = 1 To lastRow 
     Set cell = Worksheets("Sheet1").Cells(Row, 2) 
     name = Split(cell.Text, "|")(0) 

     If names.Exists(name) Then 
      names(name) = names(name) & ", " & Row 
     Else 
      names.Add name, Row 
     End If 
    Next Row 
End Sub 
+0

Вы очень хорошо разбираетесь в том, что не используете rows.count. Я думаю, это решено в ответ Тима: «UsedRange.Rows .Count "? – ZAR

+0

Да, это хорошее решение.Я думаю, именно он указал мне, что UsedRange будет содержать пустые ячейки, если они были отформатированы. Забавно, что на этот раз мы используем противоположные методы. – Alter

+1

Просто соображение, но я думаю, что у Дэвида Земенса была хорошая идея использовать строку, а не массив. Если вы когда-нибудь захотите вернуть массив, просто используйте split (names, ","). Кажется, что намного больше работы по переделке массива (вы заметили, что вы уже указали это) – Alter

Смежные вопросы