Удаление элементов в массиве, если элемент имеет определенное значение VBA
у меня есть глобальный массив, prLst() это может переменной длины.  Он принимает числа как строки "1" to Ubound(prLst).  Однако, когда пользователь вводит "0", Я хочу удалить этот элемент из списка.  У меня есть следующий код, написанный для выполнения этого:
count2 = 0
eachHdr = 1
totHead = UBound(prLst)
Do
    If prLst(eachHdr) = "0" Then
        prLst(eachHdr).Delete
        count2 = count2 + 1
    End If
    keepTrack = totHead - count2
    'MsgBox "prLst = " & prLst(eachHdr)
    eachHdr = eachHdr + 1
Loop Until eachHdr > keepTrack
это не работает. Как эффективно удалить элементы в массиве prLst если элемент "0"?
Примечание: это часть более крупной программы, для которой описание можно найти здесь: сортировка групп строк Excel VBA макрос
7 ответов
массив-это структура с определенным размером. В vba можно использовать динамические массивы, которые можно сжимать или увеличивать с помощью ReDim, но нельзя удалять элементы посередине. Из вашего примера не ясно, как Ваш массив функционально работает или как вы определяете позицию индекса (eachHdr), но у вас в основном есть 3 варианта
(A) напишите пользовательскую функцию "Удалить" для вашего массива, например (непроверенный)
Public Sub DeleteElementAt(Byval index As Integer, Byref prLst as Variant)
       Dim i As Integer
        ' Move all element back one position
        For i = index + 1 To UBound(prLst)
            prLst(i - 1) = prLst(i)
        Next
        ' Shrink the array by one, removing the last one
        ReDim Preserve prLst(Len(prLst) - 1)
End Sub
(B) просто установите "фиктивное" значение в качестве значения вместо фактического удаление элемента
If prLst(eachHdr) = "0" Then        
   prLst(eachHdr) = "n/a"
End If
(C) прекратите использовать массив и измените его на VBA.Коллекция. Коллекция-это (уникальная)структура пары ключ/значение, в которой вы можете свободно добавлять или удалять элементы из
Dim prLst As New Collection
Sub DelEle(Ary, SameTypeTemp, Index As Integer) '<<<<<<<<< pass only not fixed sized array (i don't know how to declare same type temp array in proceder)
    Dim I As Integer, II As Integer
    II = -1
    If Index < LBound(Ary) And Index > UBound(Ary) Then MsgBox "Error.........."
    For I = 0 To UBound(Ary)
        If I <> Index Then
            II = II + 1
            ReDim Preserve SameTypeTemp(II)
            SameTypeTemp(II) = Ary(I)
        End If
    Next I
    ReDim Ary(UBound(SameTypeTemp))
    Ary = SameTypeTemp
    Erase SameTypeTemp
End Sub
Sub Test()
    Dim a() As Integer, b() As Integer
    ReDim a(3)
    Debug.Print "InputData:"
    For I = 0 To UBound(a)
        a(I) = I
        Debug.Print "    " & a(I)
    Next
    DelEle a, b, 1
    Debug.Print "Result:"
    For I = 0 To UBound(a)
        Debug.Print "    " & a(I)
    Next
End Sub
при создании массива, почему бы просто не пропустить 0 и сэкономить время, чтобы беспокоиться о них позже? Как упоминалось выше, массивы не подходят для удаления.
Я довольно новичок в vba & excel - только делал это около 3 месяцев - Я думал, что поделюсь своим методом де-дублирования массива здесь, поскольку этот пост кажется ему актуальным:
этот код, если часть большего приложения, которое анализирует данные канала- Трубы перечислены в листе с номером в xxxx.1, xxxx.2, гггг.1, гггг.2 .... формат. так вот почему все манипуляции со строками существуют. в основном он собирает только номер трубы только один раз, а не .2 или .Один часть.
        With wbPreviousSummary.Sheets(1)
'   here, we will write the edited pipe numbers to a collection - then pass the collection to an array
        Dim PipeDict As New Dictionary
        Dim TempArray As Variant
        TempArray = .Range(.Cells(3, 2), .Cells(3, 2).End(xlDown)).Value
        For ele = LBound(TempArray, 1) To UBound(TempArray, 1)
            If Not PipeDict.Exists(Left(TempArray(ele, 1), Len(TempArray(ele, 1) - 2))) Then
                PipeDict.Add Key:=Left(TempArray(ele, 1), Len(TempArray(ele, 1) - 2)), _
                                                        Item:=Left(TempArray(ele, 1), Len(TempArray(ele, 1) - 2))
            End If
        Next ele
        TempArray = PipeDict.Items
        For ele = LBound(TempArray) To UBound(TempArray)
            MsgBox TempArray(ele)
        Next ele
    End With
    wbPreviousSummary.Close SaveChanges:=False
    Set wbPreviousSummary = Nothing 'done early so we dont have the information loaded in memory
использование кучи сообщений для отладки atm-im уверен, что вы измените его в соответствии с вашей собственной работой.
Я надеюсь, что люди найдут это полезным, С уважением Джо!--2-->
удаление элементов в массиве, если элемент имеет определенное значение VBA
чтобы удалить элементы в массиве с определенным условием, вы можете закодировать следующим образом
For i = LBound(ArrValue, 2) To UBound(ArrValue, 2)
    If [Certain condition] Then
        ArrValue(1, i) = "-----------------------"
    End If
Next i
StrTransfer = Replace(Replace(Replace(join(Application.Index(ArrValue(), 1, 0), ","), ",-----------------------,", ",", , , vbBinaryCompare), "-----------------------,", "", , , vbBinaryCompare), ",-----------------------", "", , , vbBinaryCompare)
ResultArray = join( Strtransfer, ",")
Я часто манипулирую 1D-массивом с Join / Split но если вам нужно удалить определенное значение в нескольких измерениях, я предлагаю вам изменить этот массив на 1D-массив, как это
strTransfer = Replace(Replace(Replace(Replace(Names.Add("A", MultiDimensionArray), Chr(34), ""), "={", ""), "}", ""), ";", ",")
'somecode to edit Array like 1st code on top of this comment
'then loop through this strTransfer to get right value in right dimension
'with split function.
вот пример кода, с помощью CopyMemory функция для выполнения задания.
предположительно "намного быстрее" (в зависимости от размера и типа массива...).
Я не автор, но я проверял это :
Sub RemoveArrayElement_Str(ByRef AryVar() As String, ByVal RemoveWhich As Long) 
'// The size of the array elements
'// In the case of string arrays, they are
'// simply 32 bit pointers to BSTR's.
Dim byteLen As Byte
'// String pointers are 4 bytes
byteLen = 4
'// The copymemory operation is not necessary unless
'// we are working with an array element that is not
'// at the end of the array
If RemoveWhich < UBound(AryVar) Then
    '// Copy the block of string pointers starting at
    ' the position after the
    '// removed item back one spot.
    CopyMemory ByVal VarPtr(AryVar(RemoveWhich)), ByVal _
        VarPtr(AryVar(RemoveWhich + 1)), (byteLen) * _
        (UBound(AryVar) - RemoveWhich)
End If
'// If we are removing the last array element
'// just deinitialize the array
'// otherwise chop the array down by one.
If UBound(AryVar) = LBound(AryVar) Then
    Erase AryVar
Else
    ReDim Preserve AryVar(LBound(AryVar) To UBound(AryVar) - 1)
End If
End Sub
- цикл через массив (вариант) добавление каждого элемента и некоторого делителя в строку, если он не соответствует тому, который вы хотите удалить - Тогда разделите строку на разделитель
tmpString=""
For Each arrElem in GlobalArray
   If CStr(arrElem) = "removeThis" Then
      GoTo SkipElem
   Else
      tmpString =tmpString & ":-:" & CStr(arrElem)
   End If
SkipElem:
Next
GlobalArray = Split(tmpString, ":-:")
очевидно, что использование строк создает некоторые ограничения, например, нужно быть уверенным в информации, уже в массиве, и как этот код делает первый элемент массива пустой, но он делает то, что мне нужно, и с немного большей работой он может быть более универсальным.
