• Hi All

    Please note that at the Chandoo.org Forums there is Zero Tolerance to Spam

    Post Spam and you Will Be Deleted as a User

    Hui...

  • When starting a new post, to receive a quicker and more targeted answer, Please include a sample file in the initial post.

Extract result from table which needs to look into 2 columns and a column header

I am looking to extract result from a table where 2 columns are needed to match, find "Fail" value from data and print header of "Fail" value from table. Please refer uploaded spreadsheet.
 

Attachments

  • Extract_Result.xlsx
    10.5 KB · Views: 16
Rather than highlight the merged cell where you want the answer, why don't you illustrate what the answer should look like ....

Also are you tied to this current layout? Are the column headers you want to return (C1:G1 in your example) related to the Angle drawn? I believe you'd be better off having your result table J2:L3, structured in a more granular manner; yes, it may be possible to break out the constituents of the cells L2 and L3, but it's a hell of a lot of formula writing to make up for poor data structure.
 
Not exactly what you are looking for perhaps. But you can easily do this with PowerQuery.

1. Load data as table into PQ
2. Highlight all the columns after Angle.
3. Unpivot columns
4. Filter Value column for Fail.
5. Rename Attribute column and remove Value column.

Voila. It's done.
upload_2017-10-13_15-20-1.png

If you need to number the failure, it becomes bit more involved.
 
Now to concatenate row values in PowerQuery.

1. Select Test Name and Angles column and group by.
2. Name new column whatever you like, and choose "All Rows" from dropdown.
3. Add custom column with formula (replace column name as needed.)
Code:
=Text.Combine([ColumnName from Step2][Attribute]," ,") & " deg"
4. Remove the aggregate column.

Done.
upload_2017-10-13_15-28-27.png
 
Rather than highlight the merged cell where you want the answer, why don't you illustrate what the answer should look like ....

Also are you tied to this current layout? Are the column headers you want to return (C1:G1 in your example) related to the Angle drawn? I believe you'd be better off having your result table J2:L3, structured in a more granular manner; yes, it may be possible to break out the constituents of the cells L2 and L3, but it's a hell of a lot of formula writing to make up for poor data structure.

I am interested in K2:L4 range. And it will be fine if I could achieve this using some formulae.
 
Now to concatenate row values in PowerQuery.

1. Select Test Name and Angles column and group by.
2. Name new column whatever you like, and choose "All Rows" from dropdown.
3. Add custom column with formula (replace column name as needed.)
Code:
=Text.Combine([ColumnName from Step2][Attribute]," ,") & " deg"
4. Remove the aggregate column.

Done.
View attachment 46428
Thank a lot, Chihiro! But I'm stuck at one step.
I did 2nd step as shown in snapshot and when hit OK. I get something else, shown in snapshot. Please correct me at 2 and 3 step.
 

Attachments

  • PowerQuery.JPG
    PowerQuery.JPG
    65.8 KB · Views: 23
Following is traditional sledgehammer VBA approach. Make sure following:
1. Adjust column references where commented in the code.
2. Test it on a backup

Code:
Public Sub BuildFailureSummary()
Dim i As Long, j As Long, lngLastCol As Long
Dim strCurrTest As String, strConcat As String, strOutput As String

Range("J:K").ClearContents                                          '\\ Columns where summary is pasted. Adjust Column Ref
lngLastCol = Cells(1, Columns.Count).End(xlToLeft).Column
'\\ Loop through rows
For i = 2 To Range("A" & Rows.Count).End(xlUp).Row
    If strCurrTest <> Range("A" & i).Value Then
        strCurrTest = Range("A" & i).Value
        If Len(strOutput) <> 0 Then Range("J" & Rows.Count).End(xlUp).Offset(0, 1).Value = strOutput '\\ Adjust Column Ref
        Range("J" & Rows.Count).End(xlUp).Offset(1, 0).Value = strCurrTest                          '\\ Adjust Column Ref
        strOutput = ""
        strConcat = ""
        For j = 3 To lngLastCol
            If Trim(LCase(Cells(i, j).Value)) = "fail" Then
                strConcat = IIf(Len(strConcat) = 0, "", strConcat & ",") & Cells(1, j).Value
            End If
        Next j
        If Not Len(strConcat) = 0 Then
            strOutput = Cells(i, 2).Value & " Draw Angle : " & strConcat
        End If
    Else
        strConcat = ""
        For j = 3 To lngLastCol
            If Trim(LCase(Cells(i, j).Value)) = "fail" Then
                strConcat = IIf(Len(strConcat) = 0, "", strConcat & ",") & Cells(1, j).Value
            End If
        Next j
        If Not Len(strConcat) = 0 Then
            If Len(strOutput) <> 0 Then
                strOutput = strOutput & vbCrLf & Cells(i, 2).Value & " Draw Angle : " & strConcat
            Else
                strOutput = Cells(i, 2).Value & " Draw Angle : " & strConcat
            End If
        End If
    End If
    If i = Range("A" & Rows.Count).End(xlUp).Row Then Range("J" & Rows.Count).End(xlUp).Offset(0, 1).Value = strOutput '\\ Adjust Column Ref
Next
End Sub
 
Following is traditional sledgehammer VBA approach. Make sure following:
1. Adjust column references where commented in the code.
2. Test it on a backup

Code:
Public Sub BuildFailureSummary()
Dim i As Long, j As Long, lngLastCol As Long
Dim strCurrTest As String, strConcat As String, strOutput As String

Range("J:K").ClearContents                                          '\\ Columns where summary is pasted. Adjust Column Ref
lngLastCol = Cells(1, Columns.Count).End(xlToLeft).Column
'\\ Loop through rows
For i = 2 To Range("A" & Rows.Count).End(xlUp).Row
    If strCurrTest <> Range("A" & i).Value Then
        strCurrTest = Range("A" & i).Value
        If Len(strOutput) <> 0 Then Range("J" & Rows.Count).End(xlUp).Offset(0, 1).Value = strOutput '\\ Adjust Column Ref
        Range("J" & Rows.Count).End(xlUp).Offset(1, 0).Value = strCurrTest                          '\\ Adjust Column Ref
        strOutput = ""
        strConcat = ""
        For j = 3 To lngLastCol
            If Trim(LCase(Cells(i, j).Value)) = "fail" Then
                strConcat = IIf(Len(strConcat) = 0, "", strConcat & ",") & Cells(1, j).Value
            End If
        Next j
        If Not Len(strConcat) = 0 Then
            strOutput = Cells(i, 2).Value & " Draw Angle : " & strConcat
        End If
    Else
        strConcat = ""
        For j = 3 To lngLastCol
            If Trim(LCase(Cells(i, j).Value)) = "fail" Then
                strConcat = IIf(Len(strConcat) = 0, "", strConcat & ",") & Cells(1, j).Value
            End If
        Next j
        If Not Len(strConcat) = 0 Then
            If Len(strOutput) <> 0 Then
                strOutput = strOutput & vbCrLf & Cells(i, 2).Value & " Draw Angle : " & strConcat
            Else
                strOutput = Cells(i, 2).Value & " Draw Angle : " & strConcat
            End If
        End If
    End If
    If i = Range("A" & Rows.Count).End(xlUp).Row Then Range("J" & Rows.Count).End(xlUp).Offset(0, 1).Value = strOutput '\\ Adjust Column Ref
Next
End Sub

Awesome. it's working fine. Formatted output to some extent:
Code:
1.5 Draw Angle : 20 deg
2 Draw Angle : 21, 24 deg
3 Draw Angle : 22 deg
4 Draw Angle : 23 deg
But it would be nice, if it's possible to get it like this:
Code:
1.5 Draw Angle : 20 deg
2.0 Draw Angle : 21, 24 deg
3.0 Draw Angle : 22 deg
4.0 Draw Angle : 23 deg
i.e. displaying draw angle to 2 digits, so that it looks good.
 
Like this:
Code:
Public Sub BuildFailureSummary()
Dim i As Long, j As Long, lngLastCol As Long
Dim strCurrTest As String, strConcat As String, strOutput As String

Range("J:K").ClearContents                                          '\\ Columns where summary is pasted. Adjust Column Ref
lngLastCol = Cells(1, Columns.Count).End(xlToLeft).Column
'\\ Loop through rows
For i = 2 To Range("A" & Rows.Count).End(xlUp).Row
    If strCurrTest <> Range("A" & i).Value Then
        strCurrTest = Range("A" & i).Value
        If Len(strOutput) <> 0 Then Range("J" & Rows.Count).End(xlUp).Offset(0, 1).Value = strOutput '\\ Adjust Column Ref
      Range("J" & Rows.Count).End(xlUp).Offset(1, 0).Value = strCurrTest                          '\\ Adjust Column Ref
      strOutput = ""
        strConcat = ""
        For j = 3 To lngLastCol
            If Trim(LCase(Cells(i, j).Value)) = "fail" Then
                strConcat = IIf(Len(strConcat) = 0, "", strConcat & ",") & Cells(1, j).Value
            End If
        Next j
        If Not Len(strConcat) = 0 Then
            strOutput = Format(Cells(i, 2).Value, "0.0") & " Draw Angle : " & strConcat & " deg"
        End If
    Else
        strConcat = ""
        For j = 3 To lngLastCol
            If Trim(LCase(Cells(i, j).Value)) = "fail" Then
                strConcat = IIf(Len(strConcat) = 0, "", strConcat & ",") & Cells(1, j).Value
            End If
        Next j
        If Not Len(strConcat) = 0 Then
            If Len(strOutput) <> 0 Then
                strOutput = strOutput & vbCrLf & Format(Cells(i, 2).Value, "0.0") & " Draw Angle : " & strConcat & " deg"
            Else
                strOutput = Format(Cells(i, 2).Value, "0.0") & " Draw Angle : " & strConcat & " deg"
            End If
        End If
    End If
    If i = Range("A" & Rows.Count).End(xlUp).Row Then Range("J" & Rows.Count).End(xlUp).Offset(0, 1).Value = strOutput '\\ Adjust Column Ref
Next
End Sub
 
Like this:
Code:
Public Sub BuildFailureSummary()
Dim i As Long, j As Long, lngLastCol As Long
Dim strCurrTest As String, strConcat As String, strOutput As String

Range("J:K").ClearContents                                          '\\ Columns where summary is pasted. Adjust Column Ref
lngLastCol = Cells(1, Columns.Count).End(xlToLeft).Column
'\\ Loop through rows
For i = 2 To Range("A" & Rows.Count).End(xlUp).Row
    If strCurrTest <> Range("A" & i).Value Then
        strCurrTest = Range("A" & i).Value
        If Len(strOutput) <> 0 Then Range("J" & Rows.Count).End(xlUp).Offset(0, 1).Value = strOutput '\\ Adjust Column Ref
      Range("J" & Rows.Count).End(xlUp).Offset(1, 0).Value = strCurrTest                          '\\ Adjust Column Ref
      strOutput = ""
        strConcat = ""
        For j = 3 To lngLastCol
            If Trim(LCase(Cells(i, j).Value)) = "fail" Then
                strConcat = IIf(Len(strConcat) = 0, "", strConcat & ",") & Cells(1, j).Value
            End If
        Next j
        If Not Len(strConcat) = 0 Then
            strOutput = Format(Cells(i, 2).Value, "0.0") & " Draw Angle : " & strConcat & " deg"
        End If
    Else
        strConcat = ""
        For j = 3 To lngLastCol
            If Trim(LCase(Cells(i, j).Value)) = "fail" Then
                strConcat = IIf(Len(strConcat) = 0, "", strConcat & ",") & Cells(1, j).Value
            End If
        Next j
        If Not Len(strConcat) = 0 Then
            If Len(strOutput) <> 0 Then
                strOutput = strOutput & vbCrLf & Format(Cells(i, 2).Value, "0.0") & " Draw Angle : " & strConcat & " deg"
            Else
                strOutput = Format(Cells(i, 2).Value, "0.0") & " Draw Angle : " & strConcat & " deg"
            End If
        End If
    End If
    If i = Range("A" & Rows.Count).End(xlUp).Row Then Range("J" & Rows.Count).End(xlUp).Offset(0, 1).Value = strOutput '\\ Adjust Column Ref
Next
End Sub

:cool:Thank you!!!
 
Try another option in formula way,

upload_2017-10-15_23-24-12.png

1] In I2, copied across to J2 and all copied down :

=IF($K2="","",LOOKUP(ROWS($1:1)-1,COUNTIF(OFFSET($C$1:$G$1,,,ROW($1:$12),),"Fail"),A$2:A$13))

2] In K2, array formula copied down :

=IFERROR(INDEX($C$1:$G$1,RIGHT(SMALL(IF(COLUMN($A$1:$E$1)*($C$2:$G$13="Fail"),ROW($A$2:$A$13)/1%+COLUMN($A$1:$E$1)),ROWS($1:1)),2)),"")

p.s. Array formula to be confirmed enter with CTRL+SHIFT+ENTER together

Regards
Bosco
 

Attachments

  • Extract_Result(1).xlsx
    17.7 KB · Views: 7
Like this:
Code:
Public Sub BuildFailureSummary()
Dim i As Long, j As Long, lngLastCol As Long
Dim strCurrTest As String, strConcat As String, strOutput As String

Range("J:K").ClearContents                                          '\\ Columns where summary is pasted. Adjust Column Ref
lngLastCol = Cells(1, Columns.Count).End(xlToLeft).Column
'\\ Loop through rows
For i = 2 To Range("A" & Rows.Count).End(xlUp).Row
    If strCurrTest <> Range("A" & i).Value Then
        strCurrTest = Range("A" & i).Value
        If Len(strOutput) <> 0 Then Range("J" & Rows.Count).End(xlUp).Offset(0, 1).Value = strOutput '\\ Adjust Column Ref
      Range("J" & Rows.Count).End(xlUp).Offset(1, 0).Value = strCurrTest                          '\\ Adjust Column Ref
      strOutput = ""
        strConcat = ""
        For j = 3 To lngLastCol
            If Trim(LCase(Cells(i, j).Value)) = "fail" Then
                strConcat = IIf(Len(strConcat) = 0, "", strConcat & ",") & Cells(1, j).Value
            End If
        Next j
        If Not Len(strConcat) = 0 Then
            strOutput = Format(Cells(i, 2).Value, "0.0") & " Draw Angle : " & strConcat & " deg"
        End If
    Else
        strConcat = ""
        For j = 3 To lngLastCol
            If Trim(LCase(Cells(i, j).Value)) = "fail" Then
                strConcat = IIf(Len(strConcat) = 0, "", strConcat & ",") & Cells(1, j).Value
            End If
        Next j
        If Not Len(strConcat) = 0 Then
            If Len(strOutput) <> 0 Then
                strOutput = strOutput & vbCrLf & Format(Cells(i, 2).Value, "0.0") & " Draw Angle : " & strConcat & " deg"
            Else
                strOutput = Format(Cells(i, 2).Value, "0.0") & " Draw Angle : " & strConcat & " deg"
            End If
        End If
    End If
    If i = Range("A" & Rows.Count).End(xlUp).Row Then Range("J" & Rows.Count).End(xlUp).Offset(0, 1).Value = strOutput '\\ Adjust Column Ref
Next
End Sub
One more update is required. I am getting many values, just want to add "-" or "to" in between consecutive values. I have many such lines in single cell.
Before:
Code:
1.0 Draw Angle : -25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-4,-3,-2,-1,0,1,2,3,4 deg
What I want:
Code:
1.0 Draw Angle : -25 to -11,-4 to 4 deg
 
What defines consecutive?
- Column Numbers that you define at the top successively A,B,C or
- Normal consecutive numbers e.g. 19,20,21 i.e. separated by 1° always.
- Do you always have angles as integers and no decimal points i.e. you don't have 19.50, 22.25 etc.
 
What defines consecutive?
- Column Numbers that you define at the top successively A,B,C or
- Normal consecutive numbers e.g. 19,20,21 i.e. separated by 1° always.
- Do you always have angles as integers and no decimal points i.e. you don't have 19.50, 22.25 etc.
Yes, these are column headers that will be consecutive.
And they will be integers only.
 
But I'm stuck at one step.

After you Group by and pull all rows, it will show Tables as value.

Now you need to use the formula given in my previous post to concatenate specific column from the table. Once that's done, the column containing table as it's value can be removed.

Edit: In your case...
Code:
=Text.Combine([Count][Failed Degree]," ,") & " deg"
 
Last edited:
I have added a function and function call.

- Make sure you read comments and edit the column references marked as before.
- Test it on a backup as usual
Code:
Public Sub BuildFailureSummary()
Dim i As Long, j As Long, lngLastCol As Long
Dim strCurrTest As String, strConcat As String, strOutput As String

Range("J:K").ClearContents                                          '\\ Columns where summary is pasted. Adjust Column Ref
lngLastCol = Cells(1, Columns.Count).End(xlToLeft).Column
'\\ Loop through rows
For i = 2 To Range("A" & Rows.Count).End(xlUp).Row
    If strCurrTest <> Range("A" & i).Value Then
        strCurrTest = Range("A" & i).Value
        If Len(strOutput) <> 0 Then Range("J" & Rows.Count).End(xlUp).Offset(0, 1).Value = strOutput '\\ Adjust Column Ref
      Range("J" & Rows.Count).End(xlUp).Offset(1, 0).Value = strCurrTest                              '\\ Adjust Column Ref
      strOutput = ""
        strConcat = ""
        For j = 3 To lngLastCol
            If Trim(LCase(Cells(i, j).Value)) = "fail" Then
                strConcat = IIf(Len(strConcat) = 0, "", strConcat & ",") & Cells(1, j).Value
            End If
        Next j
        If Not Len(strConcat) = 0 Then
            strConcat = BuildSummaryList(strConcat)                                                '\\Note Line Added
            strOutput = Format(Cells(i, 2).Value, "0.0") & " Draw Angle : " & strConcat & " deg"
        End If
    Else
        strConcat = ""
        For j = 3 To lngLastCol
            If Trim(LCase(Cells(i, j).Value)) = "fail" Then
                strConcat = IIf(Len(strConcat) = 0, "", strConcat & ",") & Cells(1, j).Value
            End If
        Next j
        If Not Len(strConcat) = 0 Then
            If Len(strOutput) <> 0 Then
                strConcat = BuildSummaryList(strConcat)                                            '\\Note Line Added
                strOutput = strOutput & vbCrLf & Format(Cells(i, 2).Value, "0.0") & " Draw Angle : " & strConcat & " deg"
            Else
                strConcat = BuildSummaryList(strConcat)                                            '\\Note Line Added
                strOutput = Format(Cells(i, 2).Value, "0.0") & " Draw Angle : " & strConcat & " deg"
            End If
        End If
    End If
    If i = Range("A" & Rows.Count).End(xlUp).Row Then Range("J" & Rows.Count).End(xlUp).Offset(0, 1).Value = strOutput '\\ Adjust Column Ref
Next
End Sub
'\\ Function to deal with the pattern
Public Function BuildSummaryList(strInput As String)
Dim varInArray, varOutArray()
Dim i As Long
varInArray = Split(strInput, ",")
If UBound(varInArray) < 2 Then
    BuildSummaryList = strInput
Else
    '\\ First Pass Find Consecutives
    ReDim varOutArray(UBound(varInArray))
    varOutArray(LBound(varOutArray)) = varInArray(LBound(varInArray))
    For i = (LBound(varInArray) + 1) To (UBound(varOutArray) - 1)
        If (CLng(varInArray(i)) - 1 = CLng(varInArray(i - 1))) And _
          (CLng(varInArray(i)) + 1 = CLng(varInArray(i + 1))) Then
          varOutArray(i) = ""
        Else
          varOutArray(i) = varInArray(i)
        End If
    Next i
    varOutArray(UBound(varOutArray)) = varInArray(UBound(varInArray))
    '\\ Second Pass Test
    For i = LBound(varOutArray) To UBound(varOutArray)
        If i < UBound(varOutArray) Then
            If Len(varOutArray(i)) > 0 Then
                BuildSummaryList = IIf(Right(BuildSummaryList, 2) = "to", BuildSummaryList, BuildSummaryList & " ") & varOutArray(i)
                If Len(varOutArray(i + 1)) = 0 Then
                    BuildSummaryList = BuildSummaryList & "to"
                End If
            End If
        Else
            BuildSummaryList = IIf(Right(BuildSummaryList, 2) = "to", BuildSummaryList, BuildSummaryList & " ") & varOutArray(i)
        End If
    Next i
    BuildSummaryList = Replace(Replace(Trim(BuildSummaryList), " ", ", "), "to", " to ")
End If
End Function
 
I have added a function and function call.

- Make sure you read comments and edit the column references marked as before.
- Test it on a backup as usual
Code:
Public Sub BuildFailureSummary()
Dim i As Long, j As Long, lngLastCol As Long
Dim strCurrTest As String, strConcat As String, strOutput As String

Range("J:K").ClearContents                                          '\\ Columns where summary is pasted. Adjust Column Ref
lngLastCol = Cells(1, Columns.Count).End(xlToLeft).Column
'\\ Loop through rows
For i = 2 To Range("A" & Rows.Count).End(xlUp).Row
    If strCurrTest <> Range("A" & i).Value Then
        strCurrTest = Range("A" & i).Value
        If Len(strOutput) <> 0 Then Range("J" & Rows.Count).End(xlUp).Offset(0, 1).Value = strOutput '\\ Adjust Column Ref
      Range("J" & Rows.Count).End(xlUp).Offset(1, 0).Value = strCurrTest                              '\\ Adjust Column Ref
      strOutput = ""
        strConcat = ""
        For j = 3 To lngLastCol
            If Trim(LCase(Cells(i, j).Value)) = "fail" Then
                strConcat = IIf(Len(strConcat) = 0, "", strConcat & ",") & Cells(1, j).Value
            End If
        Next j
        If Not Len(strConcat) = 0 Then
            strConcat = BuildSummaryList(strConcat)                                                '\\Note Line Added
            strOutput = Format(Cells(i, 2).Value, "0.0") & " Draw Angle : " & strConcat & " deg"
        End If
    Else
        strConcat = ""
        For j = 3 To lngLastCol
            If Trim(LCase(Cells(i, j).Value)) = "fail" Then
                strConcat = IIf(Len(strConcat) = 0, "", strConcat & ",") & Cells(1, j).Value
            End If
        Next j
        If Not Len(strConcat) = 0 Then
            If Len(strOutput) <> 0 Then
                strConcat = BuildSummaryList(strConcat)                                            '\\Note Line Added
                strOutput = strOutput & vbCrLf & Format(Cells(i, 2).Value, "0.0") & " Draw Angle : " & strConcat & " deg"
            Else
                strConcat = BuildSummaryList(strConcat)                                            '\\Note Line Added
                strOutput = Format(Cells(i, 2).Value, "0.0") & " Draw Angle : " & strConcat & " deg"
            End If
        End If
    End If
    If i = Range("A" & Rows.Count).End(xlUp).Row Then Range("J" & Rows.Count).End(xlUp).Offset(0, 1).Value = strOutput '\\ Adjust Column Ref
Next
End Sub
'\\ Function to deal with the pattern
Public Function BuildSummaryList(strInput As String)
Dim varInArray, varOutArray()
Dim i As Long
varInArray = Split(strInput, ",")
If UBound(varInArray) < 2 Then
    BuildSummaryList = strInput
Else
    '\\ First Pass Find Consecutives
    ReDim varOutArray(UBound(varInArray))
    varOutArray(LBound(varOutArray)) = varInArray(LBound(varInArray))
    For i = (LBound(varInArray) + 1) To (UBound(varOutArray) - 1)
        If (CLng(varInArray(i)) - 1 = CLng(varInArray(i - 1))) And _
          (CLng(varInArray(i)) + 1 = CLng(varInArray(i + 1))) Then
          varOutArray(i) = ""
        Else
          varOutArray(i) = varInArray(i)
        End If
    Next i
    varOutArray(UBound(varOutArray)) = varInArray(UBound(varInArray))
    '\\ Second Pass Test
    For i = LBound(varOutArray) To UBound(varOutArray)
        If i < UBound(varOutArray) Then
            If Len(varOutArray(i)) > 0 Then
                BuildSummaryList = IIf(Right(BuildSummaryList, 2) = "to", BuildSummaryList, BuildSummaryList & " ") & varOutArray(i)
                If Len(varOutArray(i + 1)) = 0 Then
                    BuildSummaryList = BuildSummaryList & "to"
                End If
            End If
        Else
            BuildSummaryList = IIf(Right(BuildSummaryList, 2) = "to", BuildSummaryList, BuildSummaryList & " ") & varOutArray(i)
        End If
    Next i
    BuildSummaryList = Replace(Replace(Trim(BuildSummaryList), " ", ", "), "to", " to ")
End If
End Function

Thanks a lot, again, Shrivallabha! I am just stunned with this example that how many things can be done using vba in excel.
 
Back
Top