在Excel中过滤和重新排序列


1

我有一个服务(phplist,新闻通讯管理器),该服务可导出带有多个字段的用户列表。最后,每个用户都有一个或多个他已订阅的列表。

问题是,该表未按我想要的顺序进行排序,而不是为每个列表创建一个新列,而是由每一行创建所需的列。这是一个例子:

源表

我想要的是,例如,如果我有八个新闻通讯(列表),则能够在Excel中将该表转换为可创建适当列并填充其中数据的表。上一张表转换后的结果是这样的:

命运表

或类似的内容(而不是是或空白,我可以使用是和否,无论如何)。这样,我可以按列表过滤表,这在我当前的表中是不可能的:如您所见,源表中的列可以在每行上包含不同的列表。在Excel中可以吗?

最终解决方案:

多亏了W_Whalley,我才能够找到问题的真正答案。如果有人使用过PHPList,则此新闻通讯管理器允许您下载已订阅用户的列表,但是正如我在原始问题中提到的那样,它无法为您提供他们所订阅的列表的好方法。实际上,它为您提供了最终的列,其中所有列表都位于同一单元格中。这与我考虑的问题略有不同,因为该表的一行将是:

Name | Surname |     Email    |    Lists

John | Perry | john@mail.com | List1 List3 List6 List 7

并不是

Name | Surname |     Email    |    Lists

John | Perry | john@mail.com | List1 |  List3 | List6 | List 7

我提出第二张桌子是因为我认为它更易于管理,但事实并非如此。实际上,从PHPList导出用户列表后,我必须进行一些修改以使每个列表具有不同的列。这不是必需的。

我立即导出了用户列表,解决方案是应用建议只考虑一次一列的W_Whalley公式。对几个列执行此操作。最终公式(使用示例行和列)为:

=IF(ISERROR(SEARCH(L$1,$D2)),"no","yes")

或者,在西班牙语版本的Excel(我正在使用的版本)中带有示例列:

=SI(ESERROR(HALLAR($AJ$1;$AI27));"";"SI")

希望这对外面的人有用。谢谢大家,特别是W_Whalley!


您是在执行此操作,还是将每行移动到新的表上?我不确定公式如何实现此功能,但是您可以使用VBA吗?
jonsca 2011年

可以创建一个新工作表,并且该解决方案当然可以使用VBA,尽管我不确定如何应用它。如果有VBA代码,您能否提供一些有关运行代码的细节(要求,步骤)以便获得所需的结果?
javipas 2011年

列表是真的被称为“ List1”还是其他名称。如果是这样,您可以将这些值输入,从中除去“列表”,并将它们用作数组中“是”值的索引,然后再次将整个业务写出。
jonsca 2011年

(如果它们不是这样命名的,那只是找到唯一值的问题,等等。)
jonsca 2011年

@jonsca,列表不是这样命名的,但是它们具有唯一的名称,但是我不理解您的方法。您能具体一点吗?
javipas 2011年

Answers:


1

这是非VBA解决方案。假设您最多有8个列表(可以根据需要进行调整),并且为了方便起见,您以表格开头的单元格为A1。将列表的字符串名称放在单元格L1到S1中。在单元格L2中输入此公式= IF(ISERROR(SEARCH(L $ 1,$ D2&$ E2&$ F2&$ G2&$ H2&$ I2&$ J2&$ K2)),“否”,“是”)将此公式从L1复制到S2 ,然后根据需要复制下来。

它正在做什么:SEARCH(“ listN”,[concatenated“ list1 ... list8”])返回字符串的匹配部分的起始索引号,或者(如果未找到)返回#VALUE错误(至少在LibreOffice中。)。 .sorry,没有要测试的Excel)。如果存在错误,则ISERROR函数返回“否”,否则返回“是”,即在串联的列表名称中找到字符串“ listN”。

然后,您可以使用自动过滤器功能过滤表。似乎可以处理60,000行。


W_Whalley,这似乎是一种有希望的方式来做我想做的事情。您的公式无效,但我正在尝试对其进行调整,以使其如我所愿。知道后,我将更新我的问题,谢谢!
javipas 2011年

看起来Excel SEARCH函数可能需要从一个位置开始搜索,即SEARCH(L $ 1,$ D2&$ E2&$ F2&$ G2&$ H2&$ I2&$ J2&$ K2,1)。开始位置在LibreOffice中是可选的(默认为1)。
W_Whalley

0

如果公式解决方案不符合您的要求,这是一种VBA解决方案。

我已将代码分为几个小块,以便可以分别进行解释。我包含Debug.Print命令,因此您可以了解每个块的功能。我希望我对权利有一定的解释。

Option Explicit
' "Option Explicit" means you have to explicitly declare every variable
' but you will get a "variable not declared" warning if you try to run
' your code with a misspelt variable.

Sub Rearrange()

  Dim ColOldCrnt As Integer
  Dim ColOldMax As Integer
  Dim RowCrnt As Long         ' Long in case there are more than 32767 rows
  Dim RowMax As Long          ' Use same row variable for both sheets
  Dim SheetOld() As Variant

  ' The first block of code (down to "Debug.Assert False") assumes your
  ' current list is in worksheet "Sheet1".  Change the "With Sheets()"
  ' command as necessary.

  ' The code finds the bottommost row and the rightmost column and then
  ' loads the entire rectangle to array SheetOld.  It is much faster using an
  ' array than accessing individual cells as necessary.

  With Sheets("Sheet1")
    RowMax = .Cells.Find("*", .Range("A1"), xlFormulas, , _
                                               xlByRows, xlPrevious).Row
    ColOldMax = .Cells.Find("*", .Range("A1"), xlFormulas, , _
                                         xlByColumns, xlPrevious).Column
    SheetOld = .Range(.Cells(1, 1), .Cells(RowMax, ColOldMax)).Value
  End With

  Debug.Print "Max row = " & RowMax
  Debug.Print "Max col = " & ColOldMax

  Debug.Print "First 15 rows from old sheet"
  For RowCrnt = 1 To 15
    For ColOldCrnt = 1 To ColOldMax
      ' With two dimensional arrays it is normal to have the column as the
      ' first dimension.  With arrays loaded from a worksheet, the row is
      ' the first dimension.
      Debug.Print "|" & SheetOld(RowCrnt, ColOldCrnt);
    Next
    Debug.Print "|"
  Next

  Debug.Assert False     ' This stops the routine until you press continue (F5)
                         ' Press Ctrl+G if you cannot see the Immediate Window.

  ' Normally I would put all the variables as the top but I want to discuss each
  ' block's variables separately.

  ' This block builds in array "ListName()" a list of all the names.  The list
  ' is in the order in which names are found.  If you have a mispelt name (for
  ' example: "Lsit1") you will get a column for "Lsit1".  You may have to run
  ' the routine, correct any mispelt names and then rerun.

  ' This is not top quality code.  I have had to compromise between good
  ' and easy to understand.  I hope I have the balance right.

  Dim Found As Boolean
  Dim InxNameCrnt As Integer
  Dim InxNameCrntMax As Integer
  Dim NameList() As String
  Dim NameCrnt As String

  ' Using constants makes the code a little easier to understand.
  ' I use the same constants for both the old and new sheets because
  ' the important columns are in the same sequence.
  Const ColFirstList As Integer = 4

  ReDim NameList(1 To 100)      ' Bigger than could be necessary
  InxNameCrntMax = 0

  For RowCrnt = 2 To RowMax
    For ColOldCrnt = ColFirstList To ColOldMax
      ' Get a name out of the array and trim any leading
      ' or trailing spaces
      NameCrnt = Trim(SheetOld(RowCrnt, ColOldCrnt))
      If NameCrnt <> "" Then
        Found = False
        ' Search the current list for this name
        For InxNameCrnt = 1 To InxNameCrntMax
          If NameList(InxNameCrnt) = NameCrnt Then
            ' This name already recorded
            Found = True
            Exit For      ' Exit search
          End If
        Next
        If Not Found Then
          ' Add this name to the end of the list
          InxNameCrntMax = InxNameCrntMax + 1
          NameList(InxNameCrntMax) = NameCrnt
        End If
      End If
    Next
  Next

 Debug.Print "Names in order found:"
 For InxNameCrnt = 1 To InxNameCrntMax
   Debug.Print "|" & NameList(InxNameCrnt);
 Next
 Debug.Print "|"

 Debug.Assert False     ' This stops the routine until you press continue (F5)

 ' The next block builds the output worksheet in array SheetNew().

  ' I have used "Given" and "Family" instead of "Name" and "Surname" so I
  ' can reserve "Name" for the list names.
  Const ColGiven As Integer = 1
  Const ColFamily As Integer = 2
  Const ColEmail As Integer = 3

  Dim ColNewCrnt As Integer
  Dim ColNewMax As Integer
  Dim SheetNew() As String

  ' One column for the columns to the left of the first name and then
  ' one per name.
  ReDim SheetNew(1 To RowMax, 1 To ColFirstList - 1 + InxNameCrntMax)

  ' Copy across columns heading for the first columns
  For ColNewCrnt = 1 To ColFirstList - 1
    SheetNew(1, ColNewCrnt) = SheetOld(1, ColNewCrnt)
  Next
  ' Head the remaining columns with name
  For InxNameCrnt = 1 To InxNameCrntMax
    SheetNew(1, ColFirstList - 1 + InxNameCrnt) = NameList(InxNameCrnt)
  Next

  Debug.Print "First row from new sheet:"
  For RowCrnt = 1 To 1
    For ColNewCrnt = 1 To UBound(SheetNew, 2)
      Debug.Print "|" & SheetNew(RowCrnt, ColNewCrnt);
    Next
    Debug.Print "|"
  Next

 Debug.Assert False     ' This stops the routine until you press continue (F5)

 ' This block copies information from the old sheet to the new sheet

  For RowCrnt = 2 To RowMax
    ' Copy the initial columns unchanged
    For ColNewCrnt = 1 To ColFirstList - 1
      SheetNew(RowCrnt, ColNewCrnt) = SheetOld(RowCrnt, ColNewCrnt)
    Next
    For ColOldCrnt = ColFirstList To ColOldMax
      ' Get a name out of the old sheet and trim any leading
      ' or trailing spaces
      NameCrnt = Trim(SheetOld(RowCrnt, ColOldCrnt))
      If NameCrnt <> "" Then
        Found = False
        ' Search the current list for this name
        For InxNameCrnt = 1 To InxNameCrntMax
          If NameList(InxNameCrnt) = NameCrnt Then
            ' Name found
            Found = True
            Exit For      ' Exit search
          End If
        Next
        Debug.Assert Found  ' Name found on first pass but not second
                            ' Program error
        SheetNew(RowCrnt, ColFirstList - 1 + InxNameCrnt) = "Yes"
      End If
    Next
  Next

  Debug.Print "First 15 rows from new sheet:"
  For RowCrnt = 1 To 15
    For ColNewCrnt = 1 To UBound(SheetNew, 2)
      Debug.Print "|" & SheetNew(RowCrnt, ColNewCrnt);
    Next
    Debug.Print "|"
  Next

 Debug.Assert False     ' This stops the routine until you press continue (F5)

 ' This code assumes the destination sheet is "Sheet2". Change the
 ' "With Sheets()" command if necessary

 With Sheets("Sheet2")
   .Cells.EntireRow.Delete      ' Remove everything for the sheet
   .Rows(1).Font.Bold = True     ' Set the top row to bold
   'Load the worksheet from the array
   .Range(.Cells(1, 1), .Cells(RowMax, UBound(SheetNew, 2))).Value = SheetNew

 End With

 ' I have not bothered about column widths and the columns are in the
 ' sequence found.  You could add a dummy row at the top of the old sheet
 ' for John Doe who gets every list in the sequence you require.  Alternately
 ' you could sort the rows by hand.


End Sub

我希望这一切都有意义。如果您使用这种方法,那么最好。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.