【问题标题】:Concatenate values based on multiple criteria根据多个条件连接值
【发布时间】:2019-01-15 19:29:17
【问题描述】:

我有以下数据集

Key ID  Status 1    Status 2    Order ID
1   A1  FALSE        TRUE     1234-USF-0025
1   A1  FALSE        TRUE     1234-USF-0026
1   A1  FALSE        TRUE     1234-USF-0027
2   A1  TRUE         TRUE     1234-USF-0025
2   A1  TRUE         TRUE     1234-USF-0026
2   A1  TRUE         TRUE     1234-USF-0027
3   A1  FALSE        TRUE     1234-USF-0025
3   A1  FALSE        TRUE     1234-USF-0026
3   A1  FALSE        TRUE     1234-USF-0027
4   A2  TRUE         TRUE     1234-USF-0028
4   A2  TRUE         TRUE     1234-USF-0029
4   A2  TRUE         TRUE     1234-USF-0030
5   A3  TRUE         TRUE     1234-USF-0031
5   A3  TRUE         TRUE     1234-USF-0032
5   A3  TRUE         TRUE     1234-USF-0033
6   A4  TRUE         TRUE     1234-USF-0034
6   A4  TRUE         TRUE     1234-USF-0035
6   A4  TRUE         TRUE     1234-USF-0036

我需要以下内容

Order ID        ID  TRUE    FALSE
1234-USF-0025   A1   2       1,3
1234-USF-0026   A1   2       1,3
1234-USF-0027   A1   2       1,3
1234-USF-0028   A2   4  
1234-USF-0029   A2   4  
1234-USF-0030   A2   4  
1234-USF-0031   A3   5  
1234-USF-0032   A3   5  
1234-USF-0033   A3   5  
1234-USF-0034   A4   6  
1234-USF-0035   A4   6  
1234-USF-0036   A4   6  

在第二个表(我需要的那个)中,每个Order ID 都列在对应的ID 旁边。尽管A1 在原始数据集中被列出了 9 次,但A1 总共只有 3 个唯一的Order IDs。但是,A1 也与 3 个不同的 Key 相关联。

目标是连接每个Order IDID 组合的Keys,其中Status 1Status 2 都是TRUE,并在TRUE 列中列出它们。对于Order IDID 组合,其中至少一个StatusFALSEKeys 应列在FALSE 列下。

我的尝试

  1. 我尝试从 TRUE 列开始,使用 INDEX-MATCH 作为数组公式,虽然我知道下面的公式不适用于我想要的最终目标,但我试图从小处着手并在公式的基础上进行构建.不幸的是,我对数组的了解有限,我不知道如何继续,因为我不明白为什么它会返回它所做的事情或如何从这一点上达到我的目标。

=INDEX($C$2:$C$19,MATCH(1,($H2 = $B$2:$B$19) * ($G2 = $E$2:$E$19)))

  1. 接下来,我尝试将原始数据集中的各个部分分开,但在如何继续时陷入困境。我认为这是更简单的解决方案,但我不知道如何根据所需的标准进行连接。

正确:=IF(AND($C2=TRUE,$D2=TRUE),$A2,"")

错误:=IF(OR($C2<>TRUE,$D2<>TRUE),$A2,"")

注意事项:

  • ID 至少与一个 Key 相关联,但可以有更多
  • Order ID 可以重复相同的ID,但只能重复不同的Keys 对应的ID

我也愿意接受基于VBAPythonR 的解决方案,但不知道如何为该任务启动脚本,所以我一直专注于Excel

【问题讨论】:

  • 在这种情况下,第二个表的第一列是否已经“构建”?还是每次都需要根据第一个表从头开始创建?
  • @BYates,我的解决方案是将Order ID 与另一个选项卡中的ID 连接起来,删除重复项并去掉Order ID 以粘贴到第二个表中。但是,如果有人可以提供更好的解决方案,我愿意。
  • 使用公式会很棘手,但如果您愿意接受 VBA 解决方案,我可能会提供帮助...
  • @BYates,VBA 可以正常工作!

标签: excel concatenation


【解决方案1】:

这是一个有点冗长的解决方案,并假设您的数据与您发布的完全相同(以及在 sheet1 上),但它有效(我认为)。您还需要为输出数据创建第二张工作表。如果您不确定在哪里发布此代码/如何运行它,请告诉我。

Sub DoStuff()
    'Initialize the output sheet
    Sheet2.Cells.Clear
    Sheet2.Cells(1, 1) = "Order ID"
    Sheet2.Cells(1, 2) = "ID"
    Sheet2.Cells(1, 3) = "TRUE"
    Sheet2.Cells(1, 4) = "FALSE"
    newRow = 2

    'Loop through the first sheet and remove duplicates
    lastRow = Sheet1.Range("E:E").Cells.SpecialCells(xlCellTypeConstants).Count
    For i = 2 To lastRow
        exists = False
        For j = 2 To newRow
            If Sheet1.Cells(i, 5).Value = Sheet2.Cells(j, 1).Value Then
                exists = True
                Exit For
            End If
        Next
        If exists = False Then
            Sheet2.Cells(newRow, 1) = Sheet1.Cells(i, 5).Value
            Sheet2.Cells(newRow, 2) = Sheet1.Cells(i, 2).Value
            'Populate the true and false columns
            For k = 2 To lastRow
                If Sheet1.Cells(k, 5).Value = Sheet1.Cells(i, 5).Value Then
                    If Sheet1.Cells(k, 3).Value = True And Sheet1.Cells(k, 4).Value = True Then
                        Sheet2.Cells(newRow, 3) = Sheet2.Cells(newRow, 3).Value & Sheet1.Cells(k, 1).Value & ", "
                    Else
                        Sheet2.Cells(newRow, 4) = Sheet2.Cells(newRow, 4).Value & Sheet1.Cells(k, 1).Value & ", "
                    End If
                End If
            Next
            'Remove extra characters, if there are any
            If Sheet2.Cells(newRow, 3).Value <> "" Then
                Sheet2.Cells(newRow, 3).Value = Left(Sheet2.Cells(newRow, 3).Value, Len(Sheet2.Cells(newRow, 3).Value) - 2)
            End If
            If Sheet2.Cells(newRow, 4).Value <> "" Then
                Sheet2.Cells(newRow, 4).Value = Left(Sheet2.Cells(newRow, 4).Value, Len(Sheet2.Cells(newRow, 4).Value) - 2)
            End If
            newRow = newRow + 1
        End If
    Next
End Sub

使用您发布的数据的结果:

【讨论】:

  • 谢谢!如果你不介意的话,我确实有几个简单的问题。我会在MS Excel聊天。
  • 我不确定那是什么...你能通过 cmets 在这个帖子中问你的问题吗?
  • 我倾向于在工作中使用 SO,而我的公司屏蔽了聊天室(包括您提供链接的那个),所以我无法参与。
  • 别担心,我只是想更好地理解代码,但一切都对我有用。再次感谢!
【解决方案2】:

我使用字典和类模块来帮助收集和转换数据。 它还有一个优点是更容易遵循和维护,因为命名参数或多或少是显而易见的。

我还在 VBA 数组中“完成了这项工作”,因为对于任何大型数据库,执行速度都会快得多。

在代码中定义要用于源数据和结果的工作表和范围的位置应该很明显

常规模块


Option Explicit
'Set reference to Microsoft Scripting Runtime
Sub orgOrders()
    Dim wsSrc As Worksheet, wsRes As Worksheet, rRes As Range
    Dim vSrc As Variant, vRes As Variant
    Dim dOrds As Dictionary, cOrd As cOrder
    Dim I As Long, V As Variant
    Dim sKey As String

'set source and result worksheet and range
Set wsSrc = Worksheets("Sheet2")
Set wsRes = Worksheets("Sheet2")
    Set rRes = wsRes.Cells(1, 10)

'read source data into array
With wsSrc
    vSrc = .Range(.Cells(1, 1), .Cells(.Rows.Count, 5).End(xlUp))
End With

'Read into order dictionary
Set dOrds = New Dictionary
For I = 2 To UBound(vSrc, 1)
    Set cOrd = New cOrder
    sKey = vSrc(I, 5) 'Order ID
    With cOrd
        .ID = vSrc(I, 2)
        .Key = vSrc(I, 1)
        .Status1 = vSrc(I, 3)
        .Status2 = vSrc(I, 4)
        .addTrueFalse .Key, .Status1, .Status2

    If Not dOrds.Exists(sKey) Then
        dOrds.Add Key:=sKey, Item:=cOrd
    Else
        dOrds(sKey).addTrueFalse .Key, .Status1, .Status2
    End If

    End With
Next I

'Dim Results array
ReDim vRes(0 To dOrds.Count, 1 To 4)

'Headers
    vRes(0, 1) = "Order ID"
    vRes(0, 2) = "ID"
    vRes(0, 3) = "TRUE"
    vRes(0, 4) = "FALSE"

'Data
I = 0
For Each V In dOrds.Keys
    I = I + 1
    With dOrds(V)
        vRes(I, 1) = V
        vRes(I, 2) = .ID
        vRes(I, 3) = .TrueFalse(True)
        vRes(I, 4) = .TrueFalse(False)
    End With
Next V

'Write results
Set rRes = rRes.Resize(UBound(vRes, 1) + 1, UBound(vRes, 2))
With rRes
    .EntireColumn.Clear
    .Value = vRes
    .Rows(1).Font.Bold = True
    With .EntireColumn
        .HorizontalAlignment = xlCenter
        .AutoFit
    End With
End With

End Sub

类模块

重命名此模块 cOrder


Option Explicit
Private pKey As Long
Private pID As String
Private pStatus1 As Boolean
Private pStatus2 As Boolean
Private pTrueFalse As Dictionary

Public Property Get Key() As Long
    Key = pKey
End Property
Public Property Let Key(Value As Long)
    pKey = Value
End Property

Public Property Get ID() As String
    ID = pID
End Property
Public Property Let ID(Value As String)
    pID = Value
End Property

Public Property Get Status1() As Boolean
    Status1 = pStatus1
End Property
Public Property Let Status1(Value As Boolean)
    pStatus1 = Value
End Property

Public Property Get Status2() As Boolean
    Status2 = pStatus2
End Property
Public Property Let Status2(Value As Boolean)
    pStatus2 = Value
End Property

Public Function addTrueFalse(Key As Long, Status1 As Boolean, Status2 As Boolean)
        If Status1 = True And Status2 = True Then
            If Not pTrueFalse.Exists(True) Then
                pTrueFalse.Add Key:=True, Item:=Key
            Else
                pTrueFalse(True) = pTrueFalse(True) & "," & Key
            End If
        Else
            If Not pTrueFalse.Exists(False) Then
                pTrueFalse.Add Key:=False, Item:=Key
            Else
                pTrueFalse(False) = pTrueFalse(False) & "," & Key
            End If
        End If
End Function

Public Property Get TrueFalse() As Dictionary
   Set TrueFalse = pTrueFalse
End Property


Private Sub Class_Initialize()
    Set pTrueFalse = New Dictionary
End Sub

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-21
    • 2015-03-17
    • 2015-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多