如何区分非连续范围?

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

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):

  1. Dim myRange as Range
  2. Dim c as Range, a as Range
  3. Dim t As Long, m as Long
  4. Dim delimiterItem as Variant
  5. Dim newSheetName as String
  6. newSheetName = ActiveSheet.Name
  7. delimiterItem = "_"
  8. t = 2
  9. With Sheets.Add(After:=Sheets(Sheets.Count))
  10. .Name = "Final"
  11. If myRange Is Nothing Then
  12. MsgBox ("Value not present in this workbook.")
  13. Else
  14. For Each a In myRange.Areas
  15. For Each c In a.Rows
  16. Worksheets(newSheetName).Activate
  17. c.EntireRow.Copy
  18. 'For m = Cells(myRange.Rows.Count, 1).End(xlUp).Row To 2 Step -1
  19. 'If Split(InStrRev(myRange.Cells(m, 1).Text, delimiterItem))(0) = Split(InStrRev(myRange.Cells(m - 1, 1).Text, delimiterItem))(0) Then
  20. Worksheets("Final").Activate
  21. Cells(8, t).Select
  22. Selection.PasteSpecial Paste:=xlPasteAll, Operation:=xlNone, SkipBlanks:= _
  23. False, Transpose:=True
  24. t = t + 1
  25. 'Sheets("Final").Range("B8").Offset(0, (t - 2) * 2).PasteSpecial xlPasteValues
  26. Next c
  27. Next a
  28. End If
  29. 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):

  1. Dim myRange as Range
  2. Dim c as Range, a as Range
  3. Dim t As Long, m as Long
  4. Dim delimiterItem as Variant
  5. Dim newSheetName as String
  6. newSheetName = ActiveSheet.Name
  7. delimiterItem = "_"
  8. t = 2
  9. With Sheets.Add(After:=Sheets(Sheets.Count))
  10. .Name = "Final"
  11. If myRange Is Nothing Then
  12. MsgBox ("Value not present in this workbook.")
  13. Else
  14. For Each a In myRange.Areas
  15. For Each c In a.Rows
  16. Worksheets(newSheetName).Activate
  17. c.EntireRow.Copy
  18. 'For m = Cells(myRange.Rows.Count, 1).End(xlUp).Row To 2 Step -1
  19. 'If Split(InStrRev(myRange.Cells(m, 1).Text, delimiterItem))(0) = Split(InStrRev(myRange.Cells(m - 1, 1).Text, delimiterItem))(0) Then
  20. Worksheets("Final").Activate
  21. Cells(8, t).Select
  22. Selection.PasteSpecial Paste:=xlPasteAll, Operation:=xlNone, SkipBlanks:= _
  23. False, Transpose:=True
  24. t = t + 1
  25. 'Sheets("Final").Range("B8").Offset(0, (t - 2) * 2).PasteSpecial xlPasteValues
  26. Next c
  27. Next a
  28. End If
  29. 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. 数据集的数量与数据“组”的数量总是相等的。

我的示例数据如下:

如何区分非连续范围?

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

  1. Dim inData As Variant
  2. inData = Sheet1.UsedRange.Value

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

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

接下来,主程序调用:

  1. Dim dataSetCount As Long
  2. Dim columnCount As Long
  3. dataSetCount = DetermineNumberOfSets(inData)

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

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

  1. '--- 这确定了输出数据中有多少行
  2. Dim maxDataPointsCount As Long
  3. maxDataPointsCount = UBound(inData, 2)
  4. Dim outData As Variant
  5. ReDim outData(1 To maxDataPointsCount, 1 To allSetsCount)

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

  1. Dim setNumber As Long
  2. For setNumber = 1 To dataSetCount
  3. '--- 遍历数据并提取此集的数据
  4. Dim i As Long
  5. For i = LBound(inData, 1) To UBound(inData, 1)
  6. Dim thisSetNumber As Long
  7. thisSetNumber = WhatsTheDataSet(inData(i, 1))
  8. If thisSetNumber = setNumber Then
  9. '--- 将此集复制到输出
  10. Dim j As Long
  11. For j = 1 To maxDataPointsCount
  12. outData(j, outputColumn) = inData(i, j)
  13. Next j
  14. outputColumn = outputColumn + 1
  15. End If
  16. Next i
  17. Next setNumber

这是完整的代码模块:

  1. Option Explicit
  2. Option Base 0
  3. Public Sub CollateData()
  4. Dim inData As Variant
  5. inData = Sheet1.UsedRange.Value
  6. Dim dataSetCount As Long
  7. Dim columnCount As Long
  8. dataSetCount = DetermineNumberOfSets(inData)
  9. '--- 这确定了输出数据中有多少列
  10. Dim allSetsCount As Long
  11. allSetsCount = dataSetCount * dataSetCount
  12. '--- 这确定了输出数据中有多少行
  13. Dim maxDataPointsCount As Long
  14. maxDataPointsCount = UBound(inData, 2)
  15. Dim outData As Variant
  16. ReDim outData(1 To maxDataPointsCount, 1 To allSetsCount)
  17. Dim outputColumn As Long
  18. outputColumn = 1
  19. Dim setNumber As Long
  20. For setNumber = 1 To dataSetCount
  21. '--- 遍历数据并提取此集的数据
  22. Dim i As Long
  23. For i = LBound(inData, 1) To UBound(inData, 1)
  24. Dim thisSetNumber As Long
  25. thisSetNumber = WhatsTheDataSet(inData(i, 1))
  26. If thisSetNumber = setNumber Then
  27. '--- 将此集复制到输出
  28. Dim j As Long
  29. For j = 1 To maxDataPointsCount
  30. outData(j, outputColumn) = inData(i, j)
  31. Next j
  32. outputColumn = outputColumn + 1
  33. End If
  34. Next i
  35. Next setNumber
  36. Dim outRange As Range
  37. Set outRange = Sheet2.Range("A1").Resize(UBound(outData, 1), UBound(outData, 2))
  38. outRange.Value = outData
  39. End Sub
  40. Private Function DetermineNumberOfSets(ByRef data As Variant) As Long
  41. '--- 遍历第一列中的标签,分离下划线后面的数字值以找到数据集的最大数量
  42. Dim max As Long
  43. Dim i As Long
  44. For i = LBound(data, 1) To UBound(data, 1)
  45. Dim tokens As Variant
  46. tokens = Split(data(i, 1), "_")
  47. If UBound(tokens) > 0 Then
  48. If max < tokens(1) Then max = tokens(1)
  49. End If
  50. Next i
  51. DetermineNumberOfSets = max
  52. End Function
  53. Private Function WhatsTheDataSet(ByVal label As String) As Long
  54. Dim tokens As Variant
  55. tokens = Split(label, "_")
  56. If UBound(tokens) > 0 Then
  57. WhatsTheDataSet = tokens(1)
  58. End If
  59. 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

  1. Dim inData As Variant
  2. 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:

  1. Private Function DetermineNumberOfSets(ByRef data As Variant) As Long
  2. &#39;--- runs through the labels in the first column and separates
  3. &#39; the number value following the underscore to find the maximum
  4. &#39; count of data sets
  5. Dim max As Long
  6. Dim i As Long
  7. For i = LBound(data, 1) To UBound(data, 1)
  8. Dim tokens As Variant
  9. tokens = Split(data(i, 1), &quot;_&quot;)
  10. If UBound(tokens) &gt; 0 Then
  11. If max &lt; tokens(1) Then max = tokens(1)
  12. End If
  13. Next i
  14. DetermineNumberOfSets = max
  15. End Function

So the main routine calls

  1. Dim dataSetCount As Long
  2. Dim columnCount As Long
  3. dataSetCount = DetermineNumberOfSets(inData)
  4. &#39;--- this determines how many columns are in the output data
  5. Dim allSetsCount As Long
  6. 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

  1. &#39;--- this determines how many rows are in the output data
  2. Dim maxDataPointsCount As Long
  3. maxDataPointsCount = UBound(inData, 2)
  4. Dim outData As Variant
  5. 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.

  1. Dim setNumber As Long
  2. For setNumber = 1 To dataSetCount
  3. &#39;--- run through the data and pick out the data for this set
  4. Dim i As Long
  5. For i = LBound(inData, 1) To UBound(inData, 1)
  6. Dim thisSetNumber As Long
  7. thisSetNumber = WhatsTheDataSet(inData(i, 1))
  8. If thisSetNumber = setNumber Then
  9. &#39;--- copy this set to the output
  10. Dim j As Long
  11. For j = 1 To maxDataPointsCount
  12. outData(j, outputColumn) = inData(i, j)
  13. Next j
  14. outputColumn = outputColumn + 1
  15. End If
  16. Next i
  17. Next setNumber

Here's the whole module for all the code

  1. Option Explicit
  2. Option Base 0
  3. Public Sub CollateData()
  4. Dim inData As Variant
  5. inData = Sheet1.UsedRange.Value
  6. Dim dataSetCount As Long
  7. Dim columnCount As Long
  8. dataSetCount = DetermineNumberOfSets(inData)
  9. &#39;--- this determines how many columns are in the output data
  10. Dim allSetsCount As Long
  11. allSetsCount = dataSetCount * dataSetCount
  12. &#39;--- this determines how many rows are in the output data
  13. Dim maxDataPointsCount As Long
  14. maxDataPointsCount = UBound(inData, 2)
  15. Dim outData As Variant
  16. ReDim outData(1 To maxDataPointsCount, 1 To allSetsCount)
  17. Dim outputColumn As Long
  18. outputColumn = 1
  19. Dim setNumber As Long
  20. For setNumber = 1 To dataSetCount
  21. &#39;--- run through the data and pick out the data for this set
  22. Dim i As Long
  23. For i = LBound(inData, 1) To UBound(inData, 1)
  24. Dim thisSetNumber As Long
  25. thisSetNumber = WhatsTheDataSet(inData(i, 1))
  26. If thisSetNumber = setNumber Then
  27. &#39;--- copy this set to the output
  28. Dim j As Long
  29. For j = 1 To maxDataPointsCount
  30. outData(j, outputColumn) = inData(i, j)
  31. Next j
  32. outputColumn = outputColumn + 1
  33. End If
  34. Next i
  35. Next setNumber
  36. Dim outRange As Range
  37. Set outRange = Sheet2.Range(&quot;A1&quot;).Resize(UBound(outData, 1), UBound(outData, 2))
  38. outRange.Value = outData
  39. End Sub
  40. Private Function DetermineNumberOfSets(ByRef data As Variant) As Long
  41. &#39;--- runs through the labels in the first column and separates
  42. &#39; the number value following the underscore to find the maximum
  43. &#39; count of data sets
  44. Dim max As Long
  45. Dim i As Long
  46. For i = LBound(data, 1) To UBound(data, 1)
  47. Dim tokens As Variant
  48. tokens = Split(data(i, 1), &quot;_&quot;)
  49. If UBound(tokens) &gt; 0 Then
  50. If max &lt; tokens(1) Then max = tokens(1)
  51. End If
  52. Next i
  53. DetermineNumberOfSets = max
  54. End Function
  55. Private Function WhatsTheDataSet(ByVal label As String) As Long
  56. Dim tokens As Variant
  57. tokens = Split(label, &quot;_&quot;)
  58. If UBound(tokens) &gt; 0 Then
  59. WhatsTheDataSet = tokens(1)
  60. End If
  61. End Function

答案2

得分: 0

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

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

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

此代码假设所有区域中的行和列数相等。为了更好地了解它的工作方式,我建议取消注释...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

  1. Set sh = ActiveWorkbook.Worksheets(&quot;Areas&quot;) &#39; obviously the sheet keeping areas to be transposed
  2. If Sheets(Sheets.count).Name = &quot;Final&quot; Then
  3. Set shFin = ActiveWorkbook.Worksheets(&quot;Final&quot;)
  4. shFin.UsedRange.Clear&#39; for testing reason only
  5. Else
  6. Set shFin = Sheets.Add(After:=Sheets(Sheets.count))
  7. shFin.Name = &quot;Final&quot;
  8. End If
  9. Set rngUR = sh.UsedRange
  10. &#39;define all the (really used range) of the worksheet:
  11. Set usedR = sh.Range(rngUR.Cells(1, 1).Address &amp; &quot;:&quot; &amp; _
  12. rngUR.Cells(rngUR.Rows.count, rngUR.Columns.count).Address)
  13. &#39;tricky way to separtate the areas...
  14. Set rng = usedR.SpecialCells(xlCellTypeConstants)
  15. k = 0
  16. For Each rA In rng.Areas
  17. ReDim ArTr(rA.Columns.count) &#39;redim the array used to transfer data to the &quot;Final&quot; one
  18. arVal = rA.Value &#39;load the area range in ArVal array
  19. For i = 1 To rA.Rows.count
  20. For ii = 0 To rA.Columns.count - 1
  21. ArTr(ii) = arVal(i, ii + 1) &#39;fill the transfer array (diferently for each area row)
  22. Next ii
  23. &#39;create the paste range and make the transfer:
  24. &#39;to optimize the code, it does what you explain, but not in that suggested order
  25. &#39;I mean, it firstly fill column B:B, then the column situated at how manu areas exists (once for each iteration)
  26. shFin.Range(Cells(5, 2 + k + j).Address &amp; &quot;:&quot; &amp; Cells(rA.Columns.count + 4, 2 + k + j).Address).Value = _
  27. Application.WorksheetFunction.Transpose(ArTr) &#39;: Stop
  28. k = i * rng.Areas.count &#39; used to define position of the next column to be filled
  29. Next i
  30. j = j + 1: k = 0
  31. Next
  32. 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:

确定