Копировать строки листа на новый лист на основе текста ячейки - PullRequest
0 голосов
/ 09 января 2019

У меня проблема, мне нужна помощь с привлечением Excel и VBA. Я почти ничего не знаю об Excel / VBA, и мне нужно решение для кодирования, которое помогло бы мне избежать выполнения чрезвычайно утомительной задачи сделать это вручную (например, сотни строк, которые необходимо проанализировать, чтобы одна строка могла стать несколькими строками на новом листе ). Я искал в Интернете решения, но меня просто смущают ответы (потому что я ничего не знаю о VB и использовании его для программирования макроса в Excel), поэтому я решил обратиться за помощью к своему конкретная проблема.

Вот краткое изложение: у меня есть электронная таблица, где мне нужно скопировать строки с исходного листа на целевой лист. Исходный лист имеет 2 столбца (A & B), которые можно рассматривать как пару ключ / значение, где col A содержит ключ, а col B содержит значение. Проблема заключается в значениях в столбце B. Значения могут быть либо одной строкой текста, либо нумерованным списком различных текстов

Что я хочу сделать для каждой строки в источнике:

  • разделить значения в столбце B, чтобы получить массив каждого отдельного значения (если значение представлено в виде нумерованного списка)
  • создать новые строки на целевом листе, циклически перебирая массив значений, так что будет создана новая строка, где: новая строка col A = исходная строка col A ключ и новая строка col B = текущий индекс итерации из массива разделенных значений.
  • если нумерованный список отсутствует, просто скопируйте исходную строку в целевой лист

Источник

A B key1 1. text1 2. text2 key2 1. text3

Target

A B key1 text1 key1 text2 key2 text3

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

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

Ответы [ 2 ]

0 голосов
/ 09 января 2019

Сплит Multi Line

enter image description here

Неясно, какой разделитель строк встречается в многолинейных ячейках. Выберите один, vbLf работал для меня.

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

Код

Sub SplitMultiLine()

    Const cSheet1 As Variant = "Sheet1"   ' Source Worksheet Name/Index
    Const cFirstR As Integer = 1          ' Source First Row Number
    Const cFirstC As Variant = "A"        ' Source First Column Letter/Number
    Const cLastC As Variant = "C"         ' Source Last Column Letter/Number
    Const cMulti As Integer = 2           ' Multi Column
    Const cSplit As String = vbLf         ' Split Char(vbLf, vbCrLf, vbCr)
    Const cDot As String = "."            ' Dot Char (Delimiter)

    Const cSheet2 As Variant = "Sheet1"   ' Target Worksheet Name/Index
    Const cTarget As String = "E1"        ' Target First Cell Address

    Dim vntS As Variant       ' Source Array
    Dim vntSplit As Variant   ' Split Array
    Dim vntT As Variant       ' Target Array
    Dim lastR As Long         ' Source Last Row
    Dim i As Long             ' Source Array Row Counter
    Dim j As Integer          ' Source/Target Array Column Counter
    Dim k As Long             ' Target Array Row Counter
    Dim m As Integer          ' Split Array Row Counter

    ' Paste Source Range into Source Array.
    With Worksheets(cSheet1)
        lastR = .Cells(.Rows.Count, cFirstC).End(xlUp).Row
        vntS = .Range(.Cells(cFirstR, cFirstC), .Cells(lastR, cLastC))
    End With

    ' Count the number of rows in target array.
    For i = 1 To UBound(vntS)
        k = k + UBound(Split(vntS(i, cMulti), cSplit)) + 1
    Next

    ' Write from Source to Target Array.
    ReDim vntT(1 To k, 1 To UBound(vntS, 2))
    k = 0
    For i = 1 To UBound(vntS)
        k = k + 1
        vntSplit = Split(vntS(i, cMulti), cSplit)
        For m = 0 To UBound(vntSplit)
            If InStr(vntSplit(m), cDot) > 0 Then
                vntT(k, cMulti) = Trim(Right(vntSplit(m), Len(vntSplit(m)) _
                        - InStr(vntSplit(m), cDot)))
              Else
                vntT(k, cMulti) = vntSplit(m)
            End If
            For j = 1 To UBound(vntS, 2)
                If j <> cMulti Then
                    vntT(k, j) = vntS(i, j)
                End If
            Next
            k = k + 1
        Next
        k = k - 1
    Next

    ' Paste Target Array into Target Range calculated from Target Frist Cell.
    With Worksheets(cSheet2).Range(cTarget)
        .Resize(UBound(vntT), UBound(vntT, 2)) = vntT
    End With

End Sub

Чрезмерное комментирование

Sub SplitMultiLineOverCommented()

    Const cSheet1 As Variant = "Sheet1"   ' Source Worksheet Name/Index
    Const cFirstR As Integer = 1          ' Source First Row Number
    Const cFirstC As Variant = "A"        ' Source First Column Letter/Number
    Const cLastC As Variant = "C"         ' Source Last Column Letter/Number
    Const cMulti As Integer = 2           ' Multi Column
    Const cSplit As String = vbLf         ' Split Char(vbLf, vbCrLf, vbCr)
    Const cDot As String = "."            ' Dot Char (Delimiter)

    Const cSheet2 As Variant = "Sheet1"   ' Target Worksheet Name/Index
    Const cTarget As String = "E1"        ' Target First Cell Address

    Dim vntS As Variant       ' Source Array
    Dim vntSplit As Variant   ' Split Array
    Dim vntT As Variant       ' Target Array
    Dim lastR As Long         ' Source Last Row
    Dim i As Long             ' Source Array Row Counter
    Dim j As Integer          ' Source/Target Array Column Counter
    Dim k As Long             ' Target Array Row Counter
    Dim m As Integer          ' Split Array Row Counter

    ' Paste Source Range into Source Array.
    With Worksheets(cSheet1)
        ' The last row of data is usually calculated going from the bottom up,
        ' it is like selecting the last cell and pressing CTRL UP and returning
        ' =ROW() in Excel.
        lastR = .Cells(.Rows.Count, cFirstC).End(xlUp).Row
        ' Paste a range into an array actually means copying it. The array
        ' created is a 1-based 2-dimensional array which has the same number
        ' of rows and columns as the Source Range.
        vntS = .Range(.Cells(cFirstR, cFirstC), .Cells(lastR, cLastC))
    End With

    ' Count the number of rows in Target Array.
    ' You refer to the last row of the array with UBound(vntS) which is short
    ' for UBound(vntS, 1) which reveals that it is referring to the first
    ' dimension (rows).
    For i = 1 To UBound(vntS)
        ' We are splitting the string by cSplit which is the line
        ' separator (delimiter). When you enter something into a cell and
        ' hold left Alt and press ENTER, the vbLf character is set in place
        ' of the line separator. But the data may have been imported from
        ' another system that uses another line separator. When splitting the
        ' string, a 0-based array is 'created' and its UBound is the last
        ' row, but since it is 0-based we have to add 1.
        k = k + UBound(Split(vntS(i, cMulti), cSplit)) + 1
    Next

    ' Write from Source to Target Array.
    ' After we have calculated the number of rows, we have to resize the
    ' Target Array. To avoid confusion, I always use '1 To' to be certain that
    ' it is a 1-based array. Since the number columns of the Source Array and
    ' the Target Array is the same, we use the UBound of the Source Array to
    ' resize the second dimension of the Target Array - UBound(vntS, 2) where
    ' 2 is indicating the second dimension, columns.
    ReDim vntT(1 To k, 1 To UBound(vntS, 2))
    ' We will use again k as the row counter since its value is no more
    ' needed. This is what I have many times forgotten, so maybe it is
    ' better to use a different variable.
    k = 0
    ' Loop through the columns of Source Array.
    For i = 1 To UBound(vntS)
        ' Increase the row of Target Array or e.g. align it for writing.
        k = k + 1
        ' Split the string (lines) in the Multi Column into the 0-based
        ' Split Array.
        vntSplit = Split(vntS(i, cMulti), cSplit)
        ' Loop through the values of the Split Array
        For m = 0 To UBound(vntSplit)
            ' Check if value contains cDot. The Instr function returns 0 if
            ' a string has not been found, it's like =FIND(".",A1) in Excel,
            ' except that Excel would return an error if not found.
            If InStr(vntSplit(m), cDot) > 0 Then
                ' If cDot was found then write the right part after cDot
                ' to the current row of column cMulti but trim the result
                ' (remove space before and after.
                ' It's like =TRIM(RIGHT(A1,LEN(A1)-FIND(".",A1))) in Excel.
                vntT(k, cMulti) = Trim(Right(vntSplit(m), Len(vntSplit(m)) _
                        - InStr(vntSplit(m), cDot)))
              Else
                ' If cDot was not found then just write the value to the
                ' current row.
                vntT(k, cMulti) = vntSplit(m)
            End If
            ' Loop through all columns.
            For j = 1 To UBound(vntS, 2)
                If j <> cMulti Then
                    ' Write to other columns (Not cMulti)
                    vntT(k, j) = vntS(i, j)
                End If
            Next ' Next Source/Target Array Column
            ' Increase the current row of Target Array before going to next
            ' value in Split Array.
            k = k + 1
        Next ' Next Split Array Row
        ' Since we have increased the last current row but haven't written to
        ' it, we have to decrease one row because of the "k = k + 1" right below
        ' "For i = 1 To UBound(vntS)" which increases the row of Target Array
        ' for each next row in Source Array.
        k = k - 1
    Next ' Next Source Array Row

    ' Paste Target Array into Target Range calculated from Target Frist Cell.
    ' Like we pasted a range into an array, we can also paste an array into
    ' a range, but it has to be the same size as the array, so by using
    ' the Resize method we adjust the Target Range First Cell to the Target
    ' Range, using the last row and column of the Target Array. Again,
    ' remember UBound(vntT) is short for UBound(vntT, 1) (rows).
    With Worksheets(cSheet2).Range(cTarget)
        .Resize(UBound(vntT), UBound(vntT, 2)) = vntT
    End With

End Sub
0 голосов
/ 09 января 2019

Вы можете сделать это с помощью двух формул.

Я предполагаю, что ваши данные в Sheet1.

Для первых столбцов используйте следующую формулу:

=IF(ISBLANK(Sheet1!A2),A1,Sheet1!A2)

Для второго использования:

=IFERROR(RIGHT(Sheet1!B2,LEN(Sheet1!B2)-FIND(". ",Sheet1!B2)-1),Sheet1!B2)

И заселить.

редактирование:

Первая формула будет смотреть на соответствующую ячейку в Sheet1, column A. Если оно пустое, оно будет принимать значение ячейки выше, где находится формула. Если оно не пустое, оно примет значение ячейки в Sheet1, column A, которую он только что проверил.

Вторая формула ищет строку ". " в ячейках в Sheet1 column B и удаляет ее и все слева от нее из текста. Если рассматриваемая строка (". ") не найдена (имеется в виду, что в данной ячейке нет нумерации), она вернет ошибку, поэтому все это заключено в оператор IFERROR, который возвращает значение ячейки в Sheet1 column B если сработало.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...