代码之家  ›  专栏  ›  技术社区  ›  Ahmad

如何循环表并按列标题访问行项目?

  •  5
  • Ahmad  · 技术社区  · 14 年前

    我有以下宏,需要循环使用Excel-2007表。该表有几列,我目前正在使用 Index 属性列。

    使用索引是我能找到的唯一正确索引到 fName 对象。我希望更好的选择是使用列名/标题访问特定的列。我该怎么做,甚至可以做到?

    此外,一般来说,有没有更好的方法来构造这个循环?

    Worksheets("Lists").Select
    
    Dim filesToImport As ListObject 
    Dim fName As Object
    Dim fileNameWithDate As String
    
    Dim newFileColIndex As Integer
    Dim newSheetColIndex As Integer
    Set filesToImport = ActiveSheet.ListObjects("tblSourceFiles")
    
    newFileColIndex = filesToImport.ListColumns("New File Name").Index // <- Can this be different?
    
    For Each fName In filesToImport.ListRows // Is there a better way?
        If InStr(fName.Range(1, col), "DATE") <> 0 Then
            // Need to change the ffg line to access by column name
            fileNameWithDate = Replace(fName.Range(1, newFileColIndex).value, "DATE", _
                                      Format(ThisWorkbook.names("ValDate").RefersToRange, "yyyymmdd"))
            wbName = OpenCSVFIle(fPath & fileNameWithDate)
            CopyData sourceFile:=CStr(fileNameWithDate), destFile:=destFile, destSheet:="temp"
        End If
    
    Next fName2
    
    4 回复  |  直到 6 年前
        1
  •  0
  •   Marcus Mangelsdorf user1542042    8 年前

    如果要在列标题中查找特定值,可以使用find方法。find方法返回一个范围,然后可以将该范围用作执行其余操作的引用。find方法有很多可选参数,如果需要进行更多调整,请在帮助文档中阅读它。

    Dim cellsToSearch As Range
    Dim foundColumn As Range
    Dim searchValue As String
    
    Set cellsToSearch = Sheet1.Range("A1:D1")  ' Set your cells to be examined here
    searchValue = "Whatever you're looking for goes here"
    
    Set foundColumn = cellsToSearch.Find(What:=searchValue)
    
        2
  •  33
  •   ShadowScripter gymshoe    12 年前

    前言

    我通过谷歌找到了这个,但我发现它不存在。因此,我将填写更多信息,解释正在发生的事情,并稍微优化代码。

    解释

    应该给你的答案是:
    是的,可以。事实上,这比你想象的要简单。

    我注意到你这样做了

    newfilecolindex=filestoimport.listcolumns(“new file name”).index
    < /代码> 
    
    

    它为您提供了标题“新文件名”的索引。
    然后,当您决定检查列时,您忘记了索引实际上也是相对列位置。

    所以,你应该像以前那样做,而不是列号。

    instr(fname.range(1,filestoimport.listcolumns(“column name”)),“date”)
    < /代码> 
    
    

    让我们再深入一点,用文字和图片来解释。
    在上图中,第一行显示绝对列索引,
    其中a1的列索引为1,b1的列索引为2,依此类推。

    listObject的头有自己的相对索引, 其中,在本例中,column1将具有列索引1,column2将具有列索引2,依此类推。这允许我们在引用具有数字或名称的列时使用listrow.range->code>property。

    为了更好地演示,这里有一个代码,它打印上一个图像中“column1”的relativeabsolute column index。

    public sub-example()。
    将wscurrent变暗为工作表,_
    lotable1作为列表对象,_
    lccolumns作为listcolumns
    
    设置wscurrent=activesheet
    设置lotable1=wscurrent.listObjects(“Table1”)。
    设置lccolumns=lotable1.listcolumns
    
    debug.print lccolumns(“column1”).index的relative。印刷品1
    debug.print lccolumns(“column1”).range.column'绝对值。印刷品3
    结束子
    < /代码> 
    
    

    由于listrow.range>指的是范围,因此它成为相对性问题,因为该范围在listobject>内。


    例如,要在每次迭代中引用第2列,可以这样做

    public sub-example()。
    将wscurrent变暗为工作表,_
    lotable1作为列表对象,_
    lccolumns作为listcolumns,,_
    lr当前为列表行
    
    设置wscurrent=activesheet
    设置lotable1=wscurrent.listObjects(“Table1”)。
    设置lccolumns=lotable1.listcolumns
    
    对于i=1到lotable1.listrows.count
    设置lrrcurrent=lotable1.listrows(i)
    
    '使用位置:范围(1,2)
    调试打印当前范围(1,2)
    '使用标题名称:范围(1,2)
    debug.print lcurrent.range(1,lccolumns(“column2”).index)
    '使用全局范围列值:范围(1,(4-2))
    debug.print lcurrent.range(1,(lccolumns(“column2”).range.column-lotable1.range.column))。
    '使用纯全局范围值:范围(5,4)
    debug.print wscurrent.cells(lrrcurrent.range.row,lccolumns(“column2”).range.column)
    下一个
    结束如果
    < /代码> 
    
    

    优化的代码

    正如所承诺的,这里是优化的代码。

    公共子代码()
    将wscurrentSheet变暗为工作表,_
    losourcefiles作为列表对象,_
    lccolumns作为listcolumns,_
    lr当前为列表行,_
    strfilenamedate作为字符串
    
    set wscurrentsheet=工作表(“列表”)。
    设置losourcefiles=wscurrentsheet.listObjects(“tblsourcefiles”)。
    设置lccolumns=losourcefiles.listcolumns
    
    对于i=1到losourcefiles.listrows.count
    设置lrrcurrent=losourcefiles.listrows(i)
    
    如果instr(lrcurrent.range(1,lccolumns(“column name”).index),“日期”)<gt;0,则
    strsrc=lrrcurrent.range(1,lccolumns(“new file name”).index).value
    strreplace=格式(thisworkbook.name(“valdate”).referstorange,“yyyymmdd”)
    
    strFilenameDate=替换(strsrc,“日期”,strreplace)
    wbname=opencsvfile(“路径”&strfilename日期)
    copydata源文件:=cstr(strfilenamedate),,_
    destfile:=“文件”,_
    目标工作表:=“温度”
    结束如果
    下一个
    结束子
    < /代码> 
    
    

    参考文献

    个人体验。

    msdn

    所以我要填写更多的信息,解释发生了什么,并对代码进行一些优化。

    解释

    显而易见的答案是:
    是的,可以。事实上,这比你想象的要简单。

    我注意到你这样做了

    newFileColIndex = filesToImport.ListColumns("New File Name").Index
    

    它为您提供了标题“新文件名”的索引。
    然后,当您决定检查列时,您忘记了索引实际上也是相对列位置。

    所以,你应该像以前那样做,而不是列号。

    InStr(fName.Range(1, filesToImport.ListColumns("Column Name")), "DATE")
    

    让我们再深入一点,用文字和图片来解释
    Relative column index
    在上图中,第一行显示了绝对列索引,
    其中a1的列索引为1,b1的列索引为2,依此类推。

    这个ListObject的头有自己的相对索引, 其中,在本例中,column1将具有列索引1,column2将具有列索引2,依此类推。这样我们就可以利用ListRow.Range属性在引用具有数字或名称的列时。

    为了更好地演示,这里有一个代码打印上一个图像中“第1列”的绝对列索引。

    Public Sub Example()
        Dim wsCurrent As Worksheet, _
            loTable1 As ListObject, _
            lcColumns As ListColumns
    
        Set wsCurrent = ActiveSheet
        Set loTable1 = wsCurrent.ListObjects("Table1")
        Set lcColumns = loTable1.ListColumns
    
        Debug.Print lcColumns("Column1").Index        'Relative. Prints 1
        Debug.Print lcColumns("Column1").Range.Column 'Absolute. Prints 3
    End Sub
    

    自从列表范围指的是范围,因为范围在可以通过.

    ListRow range
    例如,在每个迭代中引用第2列ListRow你可以这样做

    Public Sub Example()
        Dim wsCurrent As Worksheet, _
            loTable1 As ListObject, _
            lcColumns As ListColumns, _
            lrCurrent As ListRow
    
        Set wsCurrent = ActiveSheet
        Set loTable1 = wsCurrent.ListObjects("Table1")
        Set lcColumns = loTable1.ListColumns
    
        For i = 1 To loTable1.ListRows.Count
            Set lrCurrent = loTable1.ListRows(i)
    
            'Using position: Range(1, 2)
            Debug.Print lrCurrent.Range(1, 2)
            'Using header name: Range(1, 2)
            Debug.Print lrCurrent.Range(1, lcColumns("Column2").Index)
            'Using global range column values: Range(1, (4-2))
            Debug.Print lrCurrent.Range(1, (lcColumns("Column2").Range.Column - loTable1.Range.Column))
            'Using pure global range values: Range(5,4)
            Debug.Print wsCurrent.Cells(lrCurrent.Range.Row, lcColumns("Column2").Range.Column)
        Next i
    End If
    

    优化代码

    正如所承诺的,这里是优化的代码。

    Public Sub Code()
        Dim wsCurrentSheet As Worksheet, _
            loSourceFiles As ListObject, _
            lcColumns As ListColumns, _
            lrCurrent As ListRow, _
            strFileNameDate As String
    
        Set wsCurrentSheet = Worksheets("Lists")
        Set loSourceFiles = wsCurrentSheet.ListObjects("tblSourceFiles")
        Set lcColumns = loSourceFiles.ListColumns
    
        For i = 1 To loSourceFiles.ListRows.Count
            Set lrCurrent = loSourceFiles.ListRows(i)
    
            If InStr(lrCurrent.Range(1, lcColumns("Column Name").Index), "DATE") <> 0 Then
                strSrc = lrCurrent.Range(1, lcColumns("New File Name").Index).value
                strReplace = Format(ThisWorkbook.Names("ValDate").RefersToRange, "yyyymmdd")
    
                strFileNameDate = Replace(strSrc, "DATE", strReplace)
                wbName = OpenCSVFile("Path" & strFileNameDate)
                CopyData sourceFile:=CStr(strFileNameDate), _
                         destFile:="file", _
                         destSheet:="temp"
            End If
        Next i
    End Sub
    

    工具书类

    个人经验。

    MSDN

        3
  •  1
  •   maybeWeCouldStealAVan    10 年前

    这是一个方便的功能:

    Function rowCell(row As ListRow, col As String) As Range
        Set rowCell = Intersect(row.Range, row.Parent.ListColumns(col).Range)
    End Function
    
        4
  •  0
  •   Sancarn    6 年前

    最乐观的答案对我来说太复杂了…这可能不是最理想的代码(您需要一个特殊的类来简化和优化代码),但它将比大多数解决方案更快(可能包括最乐观的答案)。

    以下代码将列表行对象包装到集合中:

    Function lrWrap(lr As ListRow, lo As ListObject) As Collection
        Dim vh As Variant: vh = lo.HeaderRowRange.Value 'Header
        Dim vr As Variant: vr = lr.Range.Value          'This row
        Dim retCol As New Collection
    
        'Append list row and object to collection as __ListRow and __ListObject
        retCol.Add lr, "__ListRow"
        retCol.Add lo, "__ListObject"
    
        'Loop through each header and append row value with header as key into return collection
        For i = LBound(vh, 2) To UBound(vh, 2)
            retCol.Add vr(1, i), vh(1, i)
        Next
    
        'Return retCol
        Set lrWrap = retCol
    End Function
    

    最终,通过该功能,您可以执行以下操作:

    Dim MyListObject as ListObject, row as ListRow, col as Collection
    set MyListObject = Sheets("MySheet").ListObjects("MyTableName")
    For each row in MyListObject
        set col = lrWrap(row)
        debug.print col("My Table Header")
    
        'If you need to access the list object you can do so via __ListObject
        debug.print col("__ListObject").name
    next
    

    在我看来,这使得你的代码比上面的任何代码都要干净得多。