Copy value N times in Excel - excel

I have simple list:
A B
item1 3
item2 2
item3 4
item4 1
Need to output:
A
item1
item1
item1
item2
item2
item3
item3
item3
item3
item4

Here is one way of doing it without VBA:
Insert a column to the left of A, so your current A and B columns are now B and C.
Put 1 in A1
Put =A1+C1 in A2 and copy down to A5
Put an empty string in B5, by just entering a single quote (') in the cell
Put a 1 in E1, a 2 in E2, and copy down as to get 1, 2, ..., 10
Put =VLOOKUP(E1,$A$1:$B$5,2) in F1 and copy down.
It should look like this:
| A | B | C | D | E | F |
|----|-------|---|---|----|-------|
| 1 | item1 | 3 | | 1 | item1 |
| 4 | item2 | 2 | | 2 | item1 |
| 6 | item3 | 4 | | 3 | item1 |
| 10 | item4 | 1 | | 4 | item2 |
| 11 | | | | 5 | item2 |
| | | | | 6 | item3 |
| | | | | 7 | item3 |
| | | | | 8 | item3 |
| | | | | 9 | item3 |
| | | | | 10 | item4 |

Here's the VBA solution. I don't quite understand the comment that VBA won't be dynamic. It's as dynamic as you make it, just like a formula. Note that this macro will erase all data on Sheet1 and replace it with the new output. If you want the desired output on a different sheet, then change the reference to Sheet2 or what have you.
Option Explicit
Sub MultiCopy()
Dim arr As Variant
Dim r As Range
Dim i As Long
Dim currRow As Long
Dim nCopy As Long
Dim item As String
'store cell values in array
arr = Sheet1.UsedRange
currRow = 2
'remove all values
Sheet1.Cells.ClearContents
Sheet1.Range("A1") = "A"
For i = 2 To UBound(arr, 1)
item = arr(i, 1)
nCopy = arr(i, 2) - 1
If nCopy > -1 Then
Sheet1.Range("A" & currRow & ":A" & (currRow + nCopy)).Value = item
currRow = currRow + nCopy + 1
End If
Next
End Sub

Related

Get count of days from last date over time? (non-VBA)

I have two columns like so:
Item | Date
Item1 | 1/1/20
Item2 | 1/2/20
Item1 | 1/3/20
Item2 | 1/4/20
Item1 | 1/6/20
Item2 | 1/8/20
I want to be able to get a count of days passed since any item showed from its last date, like so:
Item | Date | Days passed
Item1 | 1/1/20 | 0
Item2 | 1/2/20 | 0
Item1 | 1/3/20 | 2
Item2 | 1/4/20 | 2
Item1 | 1/6/20 | 3
Item2 | 1/8/20 | 4
Any ideas that are a non-VBA solution?
=B10-LOOKUP(2,1/($A$4:A9=A10),$B$4:B9)

Retrieving name and location of specific Shapes from worksheet with VBA

This is a follow up to my previous question (Retrieving information of OLEObjects from Workbook with VBA)
Scenario: I am trying to retrieve data from a worksheet. The data might be normal strings or number or might be encased in check boxed (checked or not).
Data example:
+---------+-------+------------------+------+------------------+
| item1 | 2004 | | | |
+---------+-------+------------------+------+------------------+
| value x | rfd | checkbox for rfd | nfd | checkbox for nfd |
+---------+-------+------------------+------+------------------+
| ident | test7 | call3 | | |
+---------+-------+------------------+------+------------------+
Obs: In this example the "checkbox for rfd/nfd" is a normal checkbox (either form or activex), and depending on the item in that sheet, either might be selected.
Objective: What I am trying to do is read the worksheet in 2 steps: First read all the data that is directly called, so I use the code:
Sub Test_retrieve()
' this will get all non object values from the sheet
Dim array_test As Variant
Dim i As Long, j As Long
array_test = ThisWorkbook.Sheets(1).UsedRange
For i = 1 To ThisWorkbook.Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row
For j = 1 To ThisWorkbook.Sheets(1).Cells(1, Columns.Count).End(xlToLeft).Column
ThisWorkbook.Sheets(2).Cells(i, j) = array_test(i, j)
Next j
Next i
End Sub
to get:
+---------+-------+------------------+------+------------------+
| item1 | 2004 | | | |
+---------+-------+------------------+------+------------------+
| value x | rfd | | nfd | |
+---------+-------+------------------+------+------------------+
| ident | test7 | call3 | | |
+---------+-------+------------------+------+------------------+
Next I am trying to reach all the objectives/shapes in my worksheet. I used the following code to get name, value (checked of not) and location of all activex objects:
Sub getavticeboxvalue()
' this will get the names and values (as binary) of all the activex controlbox objects in the sheet
Dim objx As Object
Dim i As Long
i = 1
For Each objx In ThisWorkbook.Sheets(1).OLEObjects
If objx.Object.Value = True Then
ThisWorkbook.Sheets(3).Cells(i, 1).Value = 1
ThisWorkbook.Sheets(3).Cells(i, 2).Value = objx.Name
ThisWorkbook.Sheets(3).Cells(i, 3).Value = objx.BottomRightCell.Address
ElseIf objx.Object.Value = False Then
ThisWorkbook.Sheets(3).Cells(i, 1).Value = 0
ThisWorkbook.Sheets(3).Cells(i, 2).Value = objx.Name
ThisWorkbook.Sheets(3).Cells(i, 3).Value = objx.BottomRightCell.Address
End If
i = i + 1
Next objx
End Sub
Which yields something like:
+-------+-----------+----------+
| value | name | location |
+-------+-----------+----------+
| 0 | checkbox1 | $C$2 |
+-------+-----------+----------+
| 1 | checkbox2 | $E$2 |
+-------+-----------+----------+
I would then proceed to feed the values (1s and 0s), to the first table, in the place where the checkboxes originally where (location).
Issue: When I try the same procedure for Form Control (instead of activex), I have less options, and although I can look for them (ThisWorkbook.Sheets(1).Shapes.Type = 8) I cannot find their name or location.
Question: Is there a way to find their name and location? Is there a more efficient way to reach the result?
Objective:
+---------+-------+------------------+
| item1 | 2004 | | | |
+---------+-------+------------------+
| value x | rfd | 0 | nfd | 1 |
+---------+-------+------------------+
| ident | test7 | call3 | | |
+---------+-------+------------------+

Splitting multiple columns data into rows

I have about 1000 unique rows with columns to be split. I am new to VBA and not sure how to split multiple columns into rows with holding ID. The number of elements in a column can varies between rows but is the same across columns in the same row .
Input:
+----+---------------+----------+----------+
| id | col1 | col2 | col3 |
+----+---------------+----------+----------+
| 1 | abc, bcd, cde | aa,bb,cc | 1a,2b,3a |
| 2 | abc, ded | ba,de | a7,7a |
| 3 | a,b,c,d | d,c,d,a | f,d,s,a |
+----+---------------+----------+----------+
Output:
+----+------+------+------+
| id | col1 | col2 | col3 |
+----+------+------+------+
| 1 | abc | aa | 1a |
| 1 | bcd | bb | 2b |
| 1 | cde | cc | 3a |
| 2 | abc | ba | a7 |
| 2 | ded | de | 7a |
| 3 | a | d | f |
| 3 | b | c | d |
| 3 | c | d | s |
| 3 | d | a | a |
+----+------+------+------+
Your help will be greatly appreciated!
I don't know why "VBA: Split cell values into multiple rows and keep other data" this link as a previous answer but I think splitting one column and multiple columns are different questions.
Try this code (comments in code):
Sub Expand()
Dim currentRow As Long, lastRow As Long, table As Variant, i As Long, _
valuesInOneRowCol1 As Variant, valuesInOneRowCol2 As Variant, valuesInOneRowCol3 As Variant
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
currentRow = 2
'read hwole range to memory and clear the range to fill it with expanded data
table = Range("A2:D" & lastRow).Value2
Range("A2:D" & lastRow).Clear
For i = 1 To lastRow - 1
'split comma separated lists in each column
valuesInOneRowCol1 = Split(table(i, 2), ",")
valuesInOneRowCol2 = Split(table(i, 3), ",")
valuesInOneRowCol3 = Split(table(i, 4), ",")
'write all data from one row
For j = LBound(valuesInOneRowCol1) To UBound(valuesInOneRowCol1)
Cells(currentRow, 1) = table(i, 1)
Cells(currentRow, 2) = valuesInOneRowCol1(j)
Cells(currentRow, 3) = valuesInOneRowCol2(j)
Cells(currentRow, 4) = valuesInOneRowCol3(j)
currentRow = currentRow + 1
Next
Next
End Sub

How to get QTP and Excel to sort correctly?

I need to sort by columns a ascending, then b ascending, then d ascending and by columns e ascending, then f ascending, then h ascending. Using just QTP, I can't seem to get Excel to sort the data correctly.
What I want:
Table 1:
| a | b | c | d | e | f | g | h |
---------------------------------------------------------------------
| 1 | BE | blank | 51 stuff | 1 | BE | blank | 51 stuff |
| 1 | BE | blank | 100 stuff | 1 | BE | blank | 100 stuff |
| 1 | BE OF A | blank | 121 stuff | 1 | BE OF A | blank | 121 stuff |
| 1 | BE OF A | blank | 200 stuff | 1 | BE OF A | blank | 200 stuff |
| 2 | SEA | blank | 5 stuff | 1 | SEA | blank | 5 stuff |
What I got instead:
Table 2:
| a | b | c | d | e | f | g | h |
---------------------------------------------------------------------
| 1 | BE | blank | 100 stuff | 1 | BE OF A | blank | 121 stuff |
| 1 | BE | blank | 51 stuff | 1 | BE OF A | blank | 200 stuff |
| 1 | BE OF A | blank | 121 stuff | 1 | BE | blank | 100 stuff |
| 1 | BE OF A | blank | 200 stuff | 1 | BE | blank | 51 stuff |
| 2 | SEA | blank | 5 stuff | 1 | SEA | blank | 5 stuff |
Columns e through h gets populated and sorted first. Normally, the cells for those columns are populated in the correct sort order seen in Table 1. However, there have been instances where the sort order is incorrect, but that is rare. Maybe 1 out of 100,000 tries would it be populated with unsorted data.
Columns a through d gets populated and sorted last. The cells for those columns are populated in a somewhat haphazard manner. Table 3 illustrates a very simple end result without forcing a sort.
Table 3:
| a | b | c | d | e | f | g | h |
---------------------------------------------------------------------
| 1 | BE | blank | 100 stuff | 1 | BE | blank | 51 stuff |
| 1 | BE | blank | 51 stuff | 1 | BE | blank | 100 stuff |
| 1 | BE OF A | blank | 121 stuff | 1 | BE OF A | blank | 121 stuff |
| 1 | BE OF A | blank | 200 stuff | 1 | BE OF A | blank | 200 stuff |
| 2 | SEA | blank | 5 stuff | 1 | SEA | blank | 5 stuff |
What's the best way to get QTP and Excel to return the results displayed in Table 1? Is there even a way to?
Snippet of the code(s) that I'm using:
'Some Code Stuff here which leads to exporting the worksheet
rangeOne = "E1:H" & totalRowCnt
Set rangeObj = worksheetOne.Range(rangeOne)
Set range1 = excel1Obj.Range("E1")
Set range2 = excel1Obj.Range("F1")
Set range3 = excel1Obj.Range("H1")
rangeObj.Sort range1, ascend1, range2, ,ascend1, range3, ,ascend1,yes1
'Save worksheet then import sorted data back into Datatable
'and add more Code Stuff here which leads to exporting the worksheet again
rangeOne = "A1:D" & totalRowCnt
Set rangeObj = worksheetOne.Range(rangeOne)
Set range1 = excel1Obj.Range("A1")
Set range2 = excel1Obj.Range("B1")
Set range3 = excel1Obj.Range("D1")
rangeObj.Sort range1, ascend1, range2, ,ascend1, range3, ,ascend1,yes1
'Save worksheet then end script
Try loading the data into a Recordset object. You can sort the recordset like this:
rs.Sort = "a ASC, b ASC, d ASC, e ASC, f ASC, h ASC"
then copy the sorted data to Excel or write them to a CSV.
rs.MoveFirst
Do Until rs.EOF
For i = 0 To rs.Fields.Count - 1
'copy/write rs.Fields(i).Value
Next
rs.MoveNext
Loop

Excel Creating a List from Beginning and End number AND tags

I am trying to create a list from an index of grouped values.
This is very similar to this, however my groups also have "tags" on then that complicate the listings.
Here is an example of my INDEX tab:
| A | B | C | D |
-------------------------
1 | 1 | 1 | 1 | CV |
2 | 1 | 2 | 2 | IS |
3 | 1 | 3 | 3 | IS |
4 | 2 | 4 | 5 | GN |
5 | 2 | 6 | 7 | PS |
6 | 4 | 8 | 11 | SQ |
7 | 2 | 12 | 13 | SS |
8 | 1 | 14 | 14 | AT |
9 | 15 | 15 | 29 | AT |
10| 4 | 30 | 33 | TYP |
Where A is the number of pages, B is the first page, C is the last page and D is the tag. I would also like to add columns such that I can keep a running tally of the tags.
| A | B | C | D | E | F |
---------------------------------------
1 | 1 | 1 | 1 | CV | CV1 | CV1 |
2 | 1 | 2 | 2 | IS | IS1 | IS1 |
3 | 1 | 3 | 3 | IS | IS2 | IS2 |
4 | 2 | 4 | 5 | GN | GN1 | GN2 |
5 | 2 | 6 | 7 | PS | PS1 | PS2 |
6 | 4 | 8 | 11 | SQ | SQ1 | SQ4 |
7 | 2 | 12 | 13 | SS | SS1 | SS2 |
8 | 1 | 14 | 14 | AT | AT1 | AT1 |
9 | 15 | 15 | 29 | AT | AT2 | AT16 |
10| 4 | 30 | 33 | TYP | TYP1 | TYP4 |
Note that the tag could occur multiple times and it may not be in sequential rows.
Here is what I want this to look like for my LIST tab:
| A |
---------
1 | CV1 |
2 | IS1 |
3 | IS2 |
4 | GN1 |
5 | GN2 |
6 | PS1 |
7 | PS2 |
8 | SQ1 |
9 | SQ2 |
10| SQ3 |
11| SQ4 |
and so on...
How do I add the additional columns to the INDEX tab via formulas?
How do I create the LIST via formulas? (...is this even possible?)
The formulas should be pretty simple to write. Just consider what you're trying to accomplish.
Your first formula (in column E) is just taking a running count of the tags (in column D). So you want to count all cells from the first tag up to the corresponding tag where the tag names are the same. That count is to be appended to the tag name.
=$D1 & COUNTIF($D$1:$D1, $D1)
The second formula (in column F) is just taking a running sum of the page counts (in column A). So you want to take the sum of all corresponding page counts from the first tag up to the corresponding tag where the tag names are the same. The sum is to be appended to the tag name.
=$D1 & SUMIF($D$1:$D1, $D1, $A$1:$A1)
Note that the column doesn't change nor does the starting rows of the ranges (hence the need to use absolute ranges). The only thing that changes are the rows of the tag and the row of the end range.
I don't think it would be possible to generate that list through simple formulas. As far as I know, formulas need to have a 1-to-1 correspondence with another range. A single range can yield multiple values so a formula just won't cut it. You'll need to write a VBA script to generate that.
Sub GenerateList()
Dim usedRange As Range
Dim count As Dictionary
Set usedRange = Worksheets("Index").usedRange
Set count = CountValues(usedRange)
Dim output As Range
Dim row As Integer
Dim key As Variant
Set output = Worksheets("List").Columns("A").Rows
output.ClearContents
row = 1
For Each key In count.Keys()
Dim i As Integer
For i = 1 To count(key)
output(row) = key & i
row = row + 1
Next i
Next key
End Sub
Function CountValues( _
usedRange As Range, _
Optional tagsColumn As String = "D", _
Optional valuesColumn As String = "A") As Dictionary
Dim tags As Range
Dim values As Range
Set tags = usedRange.Columns(tagsColumn).Rows
Set values = usedRange.Columns(valuesColumn).Rows
Dim map As New Dictionary
Dim tag As Range
For Each tag In tags
map(tag.Value) = map(tag.Value) + values(tag.row)
Next tag
Set CountValues = map
End Function
This uses a Dictionary so you'll have to reference the scripting runtime.
It sounds like you're just trying to get a list of "Unique Values" on a separate sheet that you can use as your list. Try these pages, there are multiple VBA methods to paste unique items in a range.
Also, Advanced Filter has an option to paste unique values to another location. So none of your repeat tags would appear in this list, only unique ones for your "LIST" tab.
Anyway, not sure if that's what you're wanting, but the question was a smidge vague.
Links here:
Create Unique list
Create Unique list 2

Resources