Сопоставить больше, чем запись, в соответствии с вхождением

#excel #vba

#excel #vba

Вопрос:

У меня есть Source лист (в котором данные представлены в 7 столбцах, а основной столбец, который нужно сопоставить, — это столбец A), а на втором листе Target мне нужно разобраться с столбцом C для поиска существующих элементов и отображения результатов в соответствии с вхождениями. Я привел простой пример, чтобы объяснить проблему и улучшить представление ожидаемого результата, это Source лист введите описание изображения здесь

 Header1 Header2 Header3 Header4 Header5 Header6 Header7
101     H2_1    H3_1    H4_1    H5_1    H6_1    H7_1
102     H2_2    H3_2    H4_2    H5_2    H6_2    H7_2
103     H2_3    H3_3    H4_3    H5_3    H6_3    H7_3
102     H2_4    H3_4    H4_4    H5_4    H6_4    H7_4
101     H2_5    H3_5    H4_5    H5_5    H6_5    H7_5
103     H2_6    H3_6    H4_6    H5_6    H6_6    H7_6
105     H2_7    H3_7    H4_7    H5_7    H6_7    H7_7
104     H2_8    H3_8    H4_8    H5_8    H6_8    H7_8
  

И это ожидаемый результат
введите описание изображения здесь

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

 =IFERROR(INDEX(Source!$B$2:$G$9,AGGREGATE(15,6,ROW($C$3:$C$11)-2/($C3=Source!$A$2:$A$9),MOD((ROW($A1)-1),COUNTIF($C$3:$C$11,$C3)) 1),MATCH(Target!D$2,Source!$B$1:$G$1,0)),"")
  

Комментарии:

1. Скопируйте и вставьте данные в сообщение с изображением, чтобы мы могли копировать и вставлять в Excel. У вас есть Office365 ?

2. Обновлено. Да, у меня есть office 365, но мне нужно сделать это с помощью кода.

3. Должны ли мы понимать, что на «Целевом» листе существует только столбец C: C, как мы можем видеть на картинке?

4. Когда вы ответите на один из моих вопросов, пожалуйста, отметьте меня (@FaneDuru). В противном случае я не буду уведомлен о вашем комментарии. Я не понимаю только ваш вопрос… Я могу начать подготовку фрагмента кода только после того, как все прояснится в моей голове. Последний вопрос: может ли количество вхождений в таблице «Источник» превышать три?

5. ОК. Я подготовлю ответ. Я начал кое-что, но мне нужно было еще несколько фрагментов информации…

Ответ №1:

Попробуйте следующий код, пожалуйста. Предполагается, что на листе «Источник» (с именем «Источник») данные обрабатываются в диапазоне A2: G — последняя строка в столбце A: A, а на листе «Цель» хранится диапазон, подлежащий обработке, в диапазоне C3: C — последняя строка в столбце C: C:

 Sub testMatchEntries()
 Dim shS As Worksheet, shT As Worksheet, lastRS As Long, lastRT As Long
 Dim dict As New Scripting.Dictionary, arrS As Variant, arrT As Variant, arrInt As Variant
 Dim i As Long, j As Long, arrExcl As Variant, k As Long, El As Variant, boolFound As Boolean
 Dim iRow As Long, R As Long, arrHeaders As Variant
 
 Set shS = Worksheets("Source") 'use here your sheet, please
 Set shT = Worksheets("Target") 'use here your sheet, please
 lastRS = shS.Range("A" amp; rows.count).End(xlUp).row
 lastRT = shT.Range("C" amp; rows.count).End(xlUp).row
 shT.Range("D3:H" amp; lastRT).ClearContents 'clear the previous run filled cells
 arrS = shS.Range("A2:G" amp; lastRS).value  'put the necessary range in an array
 arrT = shT.Range("C3:F" amp; lastRT).value  'put the necessary range in an array
 'Collect the headers to be passed in the Source worksheet
 arrHeaders = Array(shS.Range("C1").value, shS.Range("D1").value, shS.Range("G1").value)
 
 ReDim arrExcl(UBound(arrT) - 1) 'create an array to exclude the already used elements
 For i = 1 To UBound(arrT)
    For Each El In arrExcl
        If El = arrT(i, 1) Then GoTo OverProcessing 'exclude the processed element for the next iteration
    Next
    For j = 1 To UBound(arrS)
        If arrT(i, 1) = arrS(j, 1) Then
            boolFound = True 'a match has been found
            If Not dict.Exists(arrT(i, 1)) Then 'fill data in a dictionary if not already esists
                dict.Add arrT(i, 1), arrS(j, 3) amp; ":" amp; arrS(j, 4) amp; ":" amp; arrS(j, 7) amp; "|1"
            Else
                'add new ranges when they are found and increment the occurrences number (last elem)
                arrInt = Split(dict(arrT(i, 1)), "|")
                dict(arrT(i, 1)) = arrInt(0) amp; ";" amp; arrS(j, 3) amp; ":" amp; _
                        arrS(j, 4) amp; ":" amp; arrS(j, 7) amp; "|" amp; CLng(arrInt(1))   1
            End If
        End If
    Next j
    If boolFound Then
        iRow = Split(dict(arrT(i, 1)), "|")(1)              'extract the number of rows

       arrInt = Split(Split(dict(arrT(i, 1)), "|")(0), ";") 'split the dict item to determine each row
       For R = 0 To iRow - 1
           arrT(i   R, 2) = Split(arrInt(R), ":")(0)        'fill the array values according to their row
           arrT(i   R, 3) = Split(arrInt(R), ":")(1)
           arrT(i   R, 4) = Split(arrInt(R), ":")(2)
       Next
    End If
    boolFound = False 'reinitialize the variable.
                      'Without it, trying to split a null string an error will occur
    arrExcl(k) = arrT(i, 1): k = k   1 'load the processed element in the exclusions array
OverProcessing:
 Next i
 shT.Range("F2:H2").value = arrHeaders 'drop the headers
 Dim sliceArray As Variant 'dim a new array to keep a slice from arrT one
 'nice line to slice a 2D array without iteration...
 sliceArray = Application.Index(arrT, Evaluate("row(1:" amp; UBound(arrT, 1) amp; ")"), _
                                                    Evaluate("COLUMN(" amp; "B:D" amp; ")"))
 'drop the processed (sliced) array:
 shT.Range("E3").Resize(UBound(sliceArray, 1), UBound(sliceArray, 2)).value = sliceArray
 MsgBox "Ready..."
End Sub
  

Он возвращает в диапазоне «E2: H» последнюю строку в столбце «C: C». Если после тестирования все работает так, как вам нужно, вы можете изменить диапазон, куда возвращаться, даже переписать столбец C: C…

Комментарии:

1. Это замечательно. Могу ли я исключить столбец C из результатов при заполнении выходного массива? Мне просто нужно, чтобы выходные данные были отделены от столбца C.

2. @YasserKhalil: Да, но самым простым способом для этого решения будет позволить коду выполнять то, что он делает, и удалять столбец только в обсуждении. Я адаптирую код для этого. Я забыл упомянуть о ссылке на «Microsoft Scripting Runtime», но если вы могли бы ее протестировать, это означает, что вы поняли, о чем идет речь… Адаптировано . Это то, что вам нужно?

3. @YasserKhalil: Но разве это не дает именно то, что вы говорите? Я имею в виду, после удаления столбца D: D (в коде). Если бы я знал об этом, скажем, запросе, я бы разработал код по-другому. Для доставки результата обработки должен потребоваться другой массив, а существующий будет ограничен только одним столбцом. Я сделал код более эффективным, используя тот же массив. Если я адаптирую его таким образом, это займет у меня почти столько же времени, поскольку это новый дизайн. Теперь я должен покинуть офис. Я также могу попробовать нарезать существующий массив, но когда я буду дома…

4. @YasserKhalil: Я адаптировал код, чтобы вырезать необходимый фрагмент из обработанного массива (хороший фрагмент кода в строке — без итерации) и удалить только данные, загруженные в фрагмент массива. Я проходил мимо друга, на короткое время, и я не мог удержаться, чтобы не применить на практике идею, которую я имел в виду… Он предоставил свой ноутбук, и вот оно решение. 🙂

5. @YasserKhalil: Добро пожаловать! Никаких особых усилий. Мне нравится излагать свои идеи в коде, если то, что нужно сделать, достаточно ясно…