如何区分非连续范围?

huangapple go评论74阅读模式
英文:

How to differentiate between noncontiguous ranges?

问题

I am trying to perform a few operations on a dynamic number of ranges that are also dynamically sized. Each time the program is run, the number of noncontiguous ranges may change, as well as the size of the ranges. I identify my ranges by finding the delimiter in Column A, which is the underscore. Right now, VBA is recognizing the ranges seen below as two different areas, but selects them both at the same time when called to find the strings containing the delimiter, which is correct. I am transposing my range areas from one page to the next. In the first column of each range, Column A, there is a name, and the rest of the row contains various numbers. In each range, the string names vary, but they all correspond to the string names in the other range(s). For instance, all the first string names in each range end with _1. The second string name in the range (or the cell in the row beneath the first string), ends in _2, for each range. Below is a picture:

如何区分非连续范围?

I know I can remove the blank row in between each range, but I don't think it is the best solution for my desired output. I would like to take the first row of the first area (in this example, A5:E5), transpose it, and paste it on a new sheet from (in this example) B5:B9. I would then like to go to the next area and do the same thing (so, copy A9:E9), transpose it, and paste it on the same sheet in the next column (so, C5:C9). Then I'd like to go back to my first range, and take the next row (A6:E6), tranpose it, and paste to D5:D9, then go to the next area and take (A10:E10), and so on until all the ranges have been pasted to the new page. So, ideally, I'd like to alternate between ranges and paste each row into a column, continuously through each range. This is my desired output:

如何区分非连续范围?

I am having a hard time getting the data the way I want it, and know that I may not have the option to alternate between ranges, so I am up to hearing any ideas. Again, the number of ranges (or areas) can change with each run, and so can the size of the ranges. For this example, I had two ranges with 3 string names, but next run, I could have three ranges with 4 string names, so I can't hard code anything. The string names will not always be the same (input_x, output_x) so I can't hard code these either, but I can search for the delimiter, the underscore in the string name, as it will always be formatted into the string name.

My current output DOES copy and paste by each cell in the area, but this is the best I have gotten with what I have tried:

如何区分非连续范围?

Here is my code (comments are some things I have been trying):

Dim myRange as Range
Dim c as Range, a as Range
Dim t As Long, m as Long
Dim delimiterItem as Variant
Dim newSheetName as String

newSheetName = ActiveSheet.Name
delimiterItem = "_"
t = 2

	With Sheets.Add(After:=Sheets(Sheets.Count))
        .Name = "Final"
        
        
        If myRange Is Nothing Then
            MsgBox ("Value not present in this workbook.")
        Else
            For Each a In myRange.Areas
                            
                For Each c In a.Rows
                    Worksheets(newSheetName).Activate
                    c.EntireRow.Copy
                    'For m = Cells(myRange.Rows.Count, 1).End(xlUp).Row To 2 Step -1
                    'If Split(InStrRev(myRange.Cells(m, 1).Text, delimiterItem))(0) = Split(InStrRev(myRange.Cells(m - 1, 1).Text, delimiterItem))(0) Then
                                  
                    Worksheets("Final").Activate
                    Cells(8, t).Select
                    Selection.PasteSpecial Paste:=xlPasteAll, Operation:=xlNone, SkipBlanks:= _
                          False, Transpose:=True
                                    
                    t = t + 1
                    'Sheets("Final").Range("B8").Offset(0, (t - 2) * 2).PasteSpecial xlPasteValues
                Next c
             Next a
        End If
    End With

Any help or pointers in the right direction would be much appreciated. Thank you!
Sorry for the length of this question.

英文:

I am trying to perform a few operations on a dynamic number of ranges that are also dynamically sized. Each time the program is run, the number of noncontiguous ranges may change, as well as the size of the ranges. I identify my ranges by finding the delimiter in Column A, which is the underscore. Right now, VBA is recognizing the ranges seen below as two different areas, but selects them both at the same time when called to find the strings containing the delimiter, which is correct. I am transposing my range areas from one page to the next. In the first column of each range, Column A, there is a name, and the rest of the row contains various numbers. In each range, the string names vary, but they all correspond to the string names in the other range(s). For instance, all the first string names in each range end with _1. The second string name in the range (or the cell in the row beneath the first string), ends in _2, for each range. Below is a picture:

如何区分非连续范围?

I know I can remove the blank row in between each range, but I don't think it is the best solution for my desired output. I would like to take the first row of the first area (in this example, A5:E5), transpose it, and paste it on a new sheet from (in this example) B5:B9. I would then like to go to the next area and do the same thing (so, copy A9:E9), transpose it, and paste it on the same sheet in the next column (so, C5:C9). Then I'd like to go back to my first range, and take the next row (A6:E6), tranpose it, and paste to D5:D9, then go to the next area and take (A10:E10), and so on until all the ranges have been pasted to the new page. So, ideally, I'd like to alternate between ranges and paste each row into a column, continuously through each range. This is my desired output:

如何区分非连续范围?

I am having a hard time getting the data the way I want it, and know that I may not have the option to alternate between ranges, so I am up to hearing any ideas. Again, the number of ranges (or areas) can change with each run, and so can the size of the ranges. For this example, I had two ranges with 3 string names, but next run, I could have three ranges with 4 string names, so I can't hard code anything. The string names will not always be the same (input_x, output_x) so I can't hard code these either, but I can search for the delimiter, the underscore in the string name, as it will always be formatted into the string name.

My current output DOES copy and paste by each cell in the area, but this is the best I have gotten with what I have tried:

如何区分非连续范围?

Here is my code (comments are some things I have been trying):

Dim myRange as Range
Dim c as Range, a as Range
Dim t As Long, m as Long
Dim delimiterItem as Variant
Dim newSheetName as String

newSheetName = ActiveSheet.Name
delimiterItem = "_"
t = 2

	With Sheets.Add(After:=Sheets(Sheets.Count))
        .Name = "Final"
        
        
        If myRange Is Nothing Then
            MsgBox ("Value not present in this workbook.")
        Else
            For Each a In myRange.Areas
                            
                For Each c In a.Rows
                    Worksheets(newSheetName).Activate
                    c.EntireRow.Copy
                    'For m = Cells(myRange.Rows.Count, 1).End(xlUp).Row To 2 Step -1
                    'If Split(InStrRev(myRange.Cells(m, 1).Text, delimiterItem))(0) = Split(InStrRev(myRange.Cells(m - 1, 1).Text, delimiterItem))(0) Then
                                  
                    Worksheets("Final").Activate
                    Cells(8, t).Select
                    Selection.PasteSpecial Paste:=xlPasteAll, Operation:=xlNone, SkipBlanks:= _
                          False, Transpose:=True
                                    
                    t = t + 1
                    'Sheets("Final").Range("B8").Offset(0, (t - 2) * 2).PasteSpecial xlPasteValues
                Next c
             Next a
        End If
    End With

Any help or pointers in the right direction would be much appreciated. Thank you!
Sorry for the length of this question.

答案1

得分: 1

这是一个使用基于内存的数组来提高速度,并帮助进行数据转置和正确大小调整的替代示例。

此算法假定:

  1. 您始终具有以“_1”开头的数据集。
  2. 数据集编号始终按顺序递增,例如“_1”、“_2”、“_3”等。
  3. 数据集的数量与数据“组”的数量总是相等的。

我的示例数据如下:

如何区分非连续范围?

首先,将数据移入基于内存的数组:

Dim inData As Variant
inData = Sheet1.UsedRange.Value

然后,基于这些假设,为了正确排序结果,您需要确定有多少个数据集。因此,我创建了这个函数,利用Split函数来获取下划线后面的数字值:

Private Function DetermineNumberOfSets(ByRef data As Variant) As Long
    '--- 遍历第一列中的标签,分离下划线后面的数字值以找到数据集的最大数量
    Dim max As Long
    Dim i As Long
    For i = LBound(data, 1) To UBound(data, 1)
        Dim tokens As Variant
        tokens = Split(data(i, 1), "_")
        If UBound(tokens) > 0 Then
            If max < tokens(1) Then max = tokens(1)
        End If
    Next i
    DetermineNumberOfSets = max
End Function

接下来,主程序调用:

Dim dataSetCount As Long
Dim columnCount As Long
dataSetCount = DetermineNumberOfSets(inData)

通过这种方式计算allSetsCount,您有效地跳过了输入数据中的任何空行。

现在创建一个数组来保存所有转置后的数据:

'--- 这确定了输出数据中有多少行
Dim maxDataPointsCount As Long
maxDataPointsCount = UBound(inData, 2)

Dim outData As Variant
ReDim outData(1 To maxDataPointsCount, 1 To allSetsCount)

最后,遍历数据以按顺序收集数据集并将数据转置到输出数组中:

Dim setNumber As Long
For setNumber = 1 To dataSetCount
    '--- 遍历数据并提取此集的数据
    Dim i As Long
    For i = LBound(inData, 1) To UBound(inData, 1)
        Dim thisSetNumber As Long
        thisSetNumber = WhatsTheDataSet(inData(i, 1))
        If thisSetNumber = setNumber Then
            '--- 将此集复制到输出
            Dim j As Long
            For j = 1 To maxDataPointsCount
                outData(j, outputColumn) = inData(i, j)
            Next j
            outputColumn = outputColumn + 1
        End If
    Next i
Next setNumber

这是完整的代码模块:

Option Explicit
Option Base 0

Public Sub CollateData()
    Dim inData As Variant
    inData = Sheet1.UsedRange.Value
    
    Dim dataSetCount As Long
    Dim columnCount As Long
    dataSetCount = DetermineNumberOfSets(inData)
    
    '--- 这确定了输出数据中有多少列
    Dim allSetsCount As Long
    allSetsCount = dataSetCount * dataSetCount
    
    '--- 这确定了输出数据中有多少行
    Dim maxDataPointsCount As Long
    maxDataPointsCount = UBound(inData, 2)
    
    Dim outData As Variant
    ReDim outData(1 To maxDataPointsCount, 1 To allSetsCount)
    
    Dim outputColumn As Long
    outputColumn = 1
    
    Dim setNumber As Long
    For setNumber = 1 To dataSetCount
        '--- 遍历数据并提取此集的数据
        Dim i As Long
        For i = LBound(inData, 1) To UBound(inData, 1)
            Dim thisSetNumber As Long
            thisSetNumber = WhatsTheDataSet(inData(i, 1))
            If thisSetNumber = setNumber Then
                '--- 将此集复制到输出
                Dim j As Long
                For j = 1 To maxDataPointsCount
                    outData(j, outputColumn) = inData(i, j)
                Next j
                outputColumn = outputColumn + 1
            End If
        Next i
    Next setNumber
    
    Dim outRange As Range
    Set outRange = Sheet2.Range("A1").Resize(UBound(outData, 1), UBound(outData, 2))
    outRange.Value = outData
End Sub

Private Function DetermineNumberOfSets(ByRef data As Variant) As Long
    '--- 遍历第一列中的标签,分离下划线后面的数字值以找到数据集的最大数量
    Dim max As Long
    Dim i As Long
    For i = LBound(data, 1) To UBound(data, 1)
        Dim tokens As Variant
        tokens = Split(data(i, 1), "_")
        If UBound(tokens) > 0 Then
            If max < tokens(1) Then max = tokens(1)
        End If
    Next i
    DetermineNumberOfSets = max
End Function

Private Function WhatsTheDataSet(ByVal label As String) As Long
    Dim tokens As Variant
    tokens = Split(label, "_")
    If UBound(tokens) > 0 Then
        WhatsTheDataSet = tokens(1)
    End If
End Function
英文:

Here is an alternative example that works using memory-based arrays for speed as well as helping with the transposition and correct sizing of the data.

This algorithm assumes:

  1. You'll always have data sets that start with _1
  2. The data set numbers always increase sequentially, e.g. _1, _2, _3 etc
  3. There will always be an equal number of data "groups" as there are data sets.

My sample data looks like this:

如何区分非连续范围?

First, I move the data into a memory-based array

Dim inData As Variant
inData = Sheet1.UsedRange.Value

Then based on these assumptions, in order to properly sort the results you need to figure out how many data sets you have. So I created this function that takes advantage of the Split function to get the number value following the underscore:

Private Function DetermineNumberOfSets(ByRef data As Variant) As Long
&#39;--- runs through the labels in the first column and separates
&#39;    the number value following the underscore to find the maximum
&#39;    count of data sets
Dim max As Long
Dim i As Long
For i = LBound(data, 1) To UBound(data, 1)
Dim tokens As Variant
tokens = Split(data(i, 1), &quot;_&quot;)
If UBound(tokens) &gt; 0 Then
If max &lt; tokens(1) Then max = tokens(1)
End If
Next i
DetermineNumberOfSets = max
End Function

So the main routine calls

Dim dataSetCount As Long
Dim columnCount As Long
dataSetCount = DetermineNumberOfSets(inData)
&#39;--- this determines how many columns are in the output data
Dim allSetsCount As Long
allSetsCount = dataSetCount * dataSetCount

By calculating allSetsCount in this way, you're effectively skipping any blank rows in your input data.

Now create an array to hold all the transposed data

&#39;--- this determines how many rows are in the output data
Dim maxDataPointsCount As Long
maxDataPointsCount = UBound(inData, 2)
Dim outData As Variant
ReDim outData(1 To maxDataPointsCount, 1 To allSetsCount)

And finally run through your data to collect the data sets in order and transpose the data to the output array.

Dim setNumber As Long
For setNumber = 1 To dataSetCount
&#39;--- run through the data and pick out the data for this set
Dim i As Long
For i = LBound(inData, 1) To UBound(inData, 1)
Dim thisSetNumber As Long
thisSetNumber = WhatsTheDataSet(inData(i, 1))
If thisSetNumber = setNumber Then
&#39;--- copy this set to the output
Dim j As Long
For j = 1 To maxDataPointsCount
outData(j, outputColumn) = inData(i, j)
Next j
outputColumn = outputColumn + 1
End If
Next i
Next setNumber

Here's the whole module for all the code

Option Explicit
Option Base 0
Public Sub CollateData()
Dim inData As Variant
inData = Sheet1.UsedRange.Value
Dim dataSetCount As Long
Dim columnCount As Long
dataSetCount = DetermineNumberOfSets(inData)
&#39;--- this determines how many columns are in the output data
Dim allSetsCount As Long
allSetsCount = dataSetCount * dataSetCount
&#39;--- this determines how many rows are in the output data
Dim maxDataPointsCount As Long
maxDataPointsCount = UBound(inData, 2)
Dim outData As Variant
ReDim outData(1 To maxDataPointsCount, 1 To allSetsCount)
Dim outputColumn As Long
outputColumn = 1
Dim setNumber As Long
For setNumber = 1 To dataSetCount
&#39;--- run through the data and pick out the data for this set
Dim i As Long
For i = LBound(inData, 1) To UBound(inData, 1)
Dim thisSetNumber As Long
thisSetNumber = WhatsTheDataSet(inData(i, 1))
If thisSetNumber = setNumber Then
&#39;--- copy this set to the output
Dim j As Long
For j = 1 To maxDataPointsCount
outData(j, outputColumn) = inData(i, j)
Next j
outputColumn = outputColumn + 1
End If
Next i
Next setNumber
Dim outRange As Range
Set outRange = Sheet2.Range(&quot;A1&quot;).Resize(UBound(outData, 1), UBound(outData, 2))
outRange.Value = outData
End Sub
Private Function DetermineNumberOfSets(ByRef data As Variant) As Long
&#39;--- runs through the labels in the first column and separates
&#39;    the number value following the underscore to find the maximum
&#39;    count of data sets
Dim max As Long
Dim i As Long
For i = LBound(data, 1) To UBound(data, 1)
Dim tokens As Variant
tokens = Split(data(i, 1), &quot;_&quot;)
If UBound(tokens) &gt; 0 Then
If max &lt; tokens(1) Then max = tokens(1)
End If
Next i
DetermineNumberOfSets = max
End Function
Private Function WhatsTheDataSet(ByVal label As String) As Long
Dim tokens As Variant
tokens = Split(label, &quot;_&quot;)
If UBound(tokens) &gt; 0 Then
WhatsTheDataSet = tokens(1)
End If
End Function

答案2

得分: 0

这是您提供的代码的翻译部分:

请尝试此代码。它仅在内存中运行,非常快速。我将尝试解释一些可能看起来奇怪的行,它们的作用是什么。它可以处理A:A列中字符串的任何字符。
这是起始工作表("Areas"):
如何区分非连续范围?
这是结果工作表("Final"):
如何区分非连续范围?

Sub testTransposeMyAreas()
    Dim sh As Worksheet, rngUR As Range, j As Long, ii As Long
    Dim rng As Range, usedR As Range, rA As Range, arVal As Variant
    Dim shFin As Worksheet, k As Long, i As Long, ArTr() As Variant
    
    Set sh = ActiveWorkbook.Worksheets("Areas") ' 显然是保存要转置的区域的工作表
    If Sheets(Sheets.Count).Name = "Final" Then
        Set shFin = ActiveWorkbook.Worksheets("Final")
        shFin.UsedRange.Clear ' 仅用于测试
    Else
        Set shFin = Sheets.Add(After:=Sheets(Sheets.Count))
        shFin.Name = "Final"
    End If
    
    Set rngUR = sh.UsedRange
    
    ' 定义工作表的(真正使用的范围):
    Set usedR = sh.Range(rngUR.Cells(1, 1).Address & ":" & _
            rngUR.Cells(rngUR.Rows.Count, rngUR.Columns.Count).Address)
    
    ' 以巧妙的方式分离区域...
    Set rng = usedR.SpecialCells(xlCellTypeConstants)
    
    k = 0
    For Each rA In rng.Areas
        ReDim ArTr(rA.Columns.Count) ' 重新定义用于将数据传输到"Final"的数组
        arVal = rA.Value ' 将区域范围加载到ArVal数组中
        
        For i = 1 To rA.Rows.Count
            For ii = 0 To rA.Columns.Count - 1
                ArTr(ii) = arVal(i, ii + 1) ' 填充传输数组(对每个区域行都有不同的方式)
            Next ii
            ' 创建粘贴范围并进行传输:
            ' 为了优化代码,它执行您解释的操作,但不是建议的顺序
            ' 我的意思是,它首先填充B:B列,然后是存在多少区域的列(每次迭代一次)
            shFin.Range(Cells(5, 2 + k + j).Address & ":" & Cells(rA.Columns.Count + 4, 2 + k + j).Address).Value = _
                Application.WorksheetFunction.Transpose(ArTr) ': Stop
            k = i * rng.Areas.Count ' 用于定义要填充的下一列的位置
        Next i
        j = j + 1: k = 0
    Next
End Sub
如果有不清楚的地方请随时提出澄清

此代码假设所有区域中的行和列数相等。为了更好地了解它的工作方式,我建议取消注释...Transpose(ArTr)之后的Stop命令,将VBE窗口缩小,并在每次停止后按F5查看工作表上发生的情况。

英文:

Please try this code. It works only in memory and is very fast. I will try to explain for some lines, which may be looking strange, what they do. It works no matter of any ("_") character of the strings in A:A column.
This is the starting sheet ("Areas"):
如何区分非连续范围?
And this is the result ("Final") sheet:
如何区分非连续范围?
Sub testTransposeMyAreas()
Dim sh As Worksheet, rngUR As Range, j As Long, ii As Long
Dim rng As Range, usedR As Range, rA As Range, arVal As Variant
Dim shFin As Worksheet, k As Long, i As Long, ArTr() As Variant

  Set sh = ActiveWorkbook.Worksheets(&quot;Areas&quot;) &#39; obviously the sheet keeping areas to be transposed
If Sheets(Sheets.count).Name = &quot;Final&quot; Then
Set shFin = ActiveWorkbook.Worksheets(&quot;Final&quot;)
shFin.UsedRange.Clear&#39; for testing reason only
Else
Set shFin = Sheets.Add(After:=Sheets(Sheets.count))
shFin.Name = &quot;Final&quot;
End If
Set rngUR = sh.UsedRange
&#39;define all the (really used range) of the worksheet:
Set usedR = sh.Range(rngUR.Cells(1, 1).Address &amp; &quot;:&quot; &amp; _
rngUR.Cells(rngUR.Rows.count, rngUR.Columns.count).Address)
&#39;tricky way to separtate the areas...
Set rng = usedR.SpecialCells(xlCellTypeConstants)
k = 0      
For Each rA In rng.Areas
ReDim ArTr(rA.Columns.count) &#39;redim the array used to transfer data to the &quot;Final&quot; one
arVal = rA.Value &#39;load the area range in ArVal array
For i = 1 To rA.Rows.count
For ii = 0 To rA.Columns.count - 1
ArTr(ii) = arVal(i, ii + 1) &#39;fill the transfer array (diferently for each area row)
Next ii
&#39;create the paste range and make the transfer:
&#39;to optimize the code, it does what you explain, but not in that suggested order
&#39;I mean, it firstly fill column B:B, then the column situated at how manu areas exists (once for each iteration)
shFin.Range(Cells(5, 2 + k + j).Address &amp; &quot;:&quot; &amp; Cells(rA.Columns.count + 4, 2 + k + j).Address).Value = _
Application.WorksheetFunction.Transpose(ArTr) &#39;: Stop
k = i * rng.Areas.count &#39; used to define position of the next column to be filled
Next i
j = j + 1: k = 0
Next
End Sub

If something unclear, do not hesitate to ask for clarifications.

This code start from the assumption that all rows and columns number in an area are equal.

In order to better see how it works, I would suggest to un-comment the Stop command after ...Transpose(ArTr), make VBE window smaller and see what is happening on the worksheet, pressing F5 after each stop.

huangapple
  • 本文由 发表于 2020年1月7日 01:09:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/59616222.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定