Concatenate Strings with Do Loop with dynamic column and files - vba

So at Assign sheet I indicate the sheets to take data for each group (each for column and the first row is the explanation of the group). I dynamically can add res at the file or delete
After I use predefined codes to assign which type of discounts / day are applied. At the example I only put two codes (C and S) and one week. For example the raw sheet data for designations Red and Black.
Data product worksheet
Then at Diary I want to show the result of concatenate the B1 value (name of product) each time code fromm price are indicated into the rows. Also I use two loop because at raw product data I have one column for price but at the Diary I have two
Summary page
This is what I finally want to get and doing like that because my boss don't know anything for code and he wan't edite it so I try to do ll dynamic at the sheets :) [I only put two images because i don't have reputations point enough to put more]
With the formula I only get FALSE as answer :(, and I need to get what you can see at summary page
Sub Diary()
Dim I As Integer, x As Integer, y As Integer, z As Integer, n As Integer
Dim p As Integer, d As Integer, f As Integer
Dim a As String, b As String
Dim element As Variant
' Initialize variables I and y at 3 and 4 to begin to show the data at the column I desire. Also x and z were intended to pass the one column mode data sheet to the two column mode at the summary page.
I = 3
x = 1
y = 4
z = 0
With Worksheets("Asign")
.Activate
.Range("B2").Select
End With
' Set the size of Data with sheet names it get form the page assign. It can dynamically changed as size as names of sheets
With ActiveSheet
r = .Cells(.Rows.Count, "B").End(xlUp).Row
End With
Dim Data() As String
ReDim Data(r)
For p = 1 To r - 1
Data(p) = ActiveSheet.Cells(p + 1, "B").Value
Next p
With Worksheets("Diary")
.Activate
.Range("C7").Select
End With
' At Diary concatenate the same cell for all the sheets I have his name stored at Data() and then pass to the next cell with data at raw data sheets (in the images (Red, Black ,... pages). In this case search for code S
Do
Cells(7, I).Select
ActiveCell.Value = ActiveCell.FormulaR1C1 = "=CONCATENATE(" & a & ")"
For Each element In Data
b = ActiveCell.FormulaR1C1 = "IF(Data& !R[-2]C[" & x & "]=""S"",CONCATENATE(Data&!R1C2,"", ""),"""")"
a = b & ";" & b
Next element
x = x - 1
I = I + 2
Loop While I < 4
' The same for the second column of summary sheet called Diary. In this case search for code C
Do
Cells(7, y).Select
ActiveCell.Value = ActiveCell.FormulaR1C1 = _
"=CONCATENATE(" & a & ")"
For Each element In Data
b = ActiveCell.FormulaR1C1 = "IF(Data& !R[-2]C[" & z & "]=""S"",CONCATENATE(Data&!R1C2,"", ""),"""")"
a = b & ";" & b
Next element
z = z - 1
y = y + 2
Loop While I < 4
' Drag and Drop the formula to all the sheet's cells you need
Range("C8:E8").Select
Selection.AutoFill Destination:=Range("C8:E10"), Type:=xlFillDefault
'
End Sub

try this. .... could be simplified by looping through "colors" .... black, red, etc
Sub Diary()
Dim red As Variant
red = Sheets("red").Range("d6:g12") ' put range data into an array for processing
Dim black As Variant
black = Sheets("black").Range("d6:g12")
Dim i As Integer
Dim j As Integer
Dim strC As String
Dim strS As String
For i = 1 To 7
For j = 1 To 4
strC = ""
strS = ""
If LCase(black(i, j)) = "c" Then strC = "Black"
If LCase(black(i, j)) = "s" Then strS = "Black"
If LCase(red(i, j)) = "c" Then
If Len(strC) > 1 Then strC = strC & ";"
strC = strC & "Red"
End If
If LCase(red(i, j)) = "s" Then
If Len(strS) > 1 Then strS = strS & ";"
strS = strS & "Red"
End If
Sheets("diary").Range("c7").Cells(i, j * 2 - 1) = strS
Sheets("diary").Range("c7").Cells(i, j * 2) = strC
Next j
Next i
End Sub

Related

Setting graph series in excel VBA with Dictionary Keys

Objective:
Dynamically generating a (100% Stacked) graph based on data in a spreadsheet.
Conditions:
I have a list sites with repetitive milestones (each site uses the same 4 milestones, but the milestones differ between projects. This functionality will be used in the trackers for several projects).
Current State:
It's drawing the stacked barchart as desired, but I cant seem to get the legend (series) to be renamed to the unique keys in the dictionary that is being built from the identified milestones.
Data Setup:
Columns X3 and beyond has the list of milestones. there are 40 records (2 blank lines) with 4 unique values. The d1 dictionary contains the unique 4 values as displayed by the output into column R (for testing only).
Image: List of data and location/milestones
All code pertaining to drawing the graph:
With Worksheets("Sheet1")
.Columns.EntireColumn.Hidden = False 'Unhide all columns.
.Rows.EntireRow.Hidden = False 'Unhide all rows.
.AutoFilterMode = False
lastrow = Range("W" & Rows.Count).End(xlUp).Row
'If MsgBox("Lastrow is: " & lastrow, vbYesNo) = vbNo Then Exit Sub
End With
Dim MyLocationCount As Integer
Dim MyMilestoneCount As Integer
'Use VbA code to find the unique values in the array with locations.
'GET ARRAY OF UNIQUE LOCATIONS
Worksheets("Sheet1").Range("W3:W" & lastrow).Select
Dim d As Object, c As Range, k, tmp As String
Set d = CreateObject("scripting.dictionary")
For Each c In Selection
tmp = Trim(c.Value)
If Len(tmp) > 0 Then d(tmp) = d(tmp) + 1
Next c
For Each k In d.Keys
Debug.Print k, d(k)
MyLocationCount = MyLocationCount + 1
Next k
Range("U1:U" & d.Count) = Application.Transpose(d.Keys) '<-- For verification of the locations keys only.
'MsgBox (MyLocationCount)
'SET ARRAY CATEGORY VALUES
Dim d3 As Object
Set d3 = CreateObject("scripting.dictionary")
x = 0
Do
x = x + 1
d3.Add key:=x, Item:=1
'MsgBox "Key " & x & ": " & d3(x) & " Key Count: " & d3.Count
Loop Until x = MyLocationCount
Dim k3 As Variant
For Each k3 In d3.Keys
' Print key and value
Debug.Print k3, d3(k3)
Next
'------------
Range("T1:T" & d3.Count) = Application.Transpose(d3.Items)'<-- For verification of the locations items only.
'GET ARRAY OF UNIQUE MILESTONES
Worksheets("Sheet1").Range("X3:X" & lastrow).Select
Dim d1 As Object, c1 As Range, k1, tmp1 As String
Set d1 = CreateObject("scripting.dictionary")
For Each c1 In Selection
tmp1 = Trim(c1.Value)
If Len(tmp1) > 0 Then d1(tmp1) = d1(tmp1) + 1
Next c1
For Each k1 In d1.Keys
Debug.Print k1, d1(k1)
MyMilestoneCount = MyMilestoneCount + 1
Next k1
Range("R1:R" & d1.Count) = Application.Transpose(d1.Keys) '<-- For verification of the milestone keys only.
ActiveSheet.ChartObjects("Chart 2").Activate
'Delete all current series of data.
Dim n As Long
With ActiveChart
For n = .SeriesCollection.Count To 1 Step -1
.SeriesCollection(n).Delete
Next n
End With
'==== START PROBLEM AREA =====
'Loop the XValues and Values code as many times as you have series. make sure to increment the collection counter. Use array values to hardcode the categories.
x = 0
Do Until x = MyMilestoneCount
With ActiveChart.SeriesCollection.NewSeries
.XValues = Array(d.Keys)
.Values = Array(d3.Items)
x = x + 1
End With
'NAME MILESTONE
'MsgBox (d1.keys(x))
ActiveChart.FullSeriesCollection(x).Name = "=""Milestone " & x & """" '<==== THIS WORKS BUT IS NOT DESIRED.
'ActiveChart.FullSeriesCollection(x).Name = d1.Keys(x) '<==== THIS IS WHAT IM TRYING TO GET TO WORK.
Loop
'==== END PROBLEM AREA =====
ActiveChart.Location Where:=xlLocationAsObject, Name:="Sheet1"
'SET LEGEND SIZE
ActiveChart.Legend.Select
Selection.Left = 284.71
Selection.Width = 69.289
Selection.Height = 144.331
Selection.Top = 9.834
Selection.Height = 157.331
With ActiveSheet.ChartObjects("Chart 2").Chart.Axes(xlValue, xlPrimary)
'.Border.LineStyle = xlNone
.MajorTickMark = xlNone
.MinorTickMark = xlNone
.TickLabelPosition = xlNone
End With
End Sub
Anyone any idea on how to use the d1 keys instead of the manual naming? (See the <=== arrows).
I have code on how to color each section of the barchart based on the data that is determined in the spreadsheet (see image). right now my main challenge is getting the series properly named.
Thanks and have a great day!
Okki

Create Sub array variable name sheet

I have one array with Sheet Names called SheetNames and I want to generate a sub array of it that only returns True at the condition (IF). I try to have a loop into a cell value onto different sheets, evaluating condition cell.value = "S". When checks that for the first D column (z = 4) I want to make the same check (IF condition) for columns D to DR at the same row.
I need to get similar result if I use formula at
Diary!C7
= IF (element!D6 = "S",CONCATENATE (element!B1, ", "), ""),
IF (element1!D6 = "S",CONCATENATE (element1!B1, ", "), ""), ....
IF (element!E6 = "S",CONCATENATE (element!B1, ", "), ""),
IF (element1!E6 = "S",CONCATENATE (element1!B1, ", "), "") .... )
Where element is a sheet name taken from an array with the sheet names who get the condition (Code S or another code).
SheetNames is one array with all the book sheets and FSheet (Filtered Sheet with condition) an array with only the filtered (with condition IF). When I can populate FSheet array for each sheet I test the condition then I must concatenate it's values at another sheet/cell and began the test condition again to the next cell (E6) ... But I'm trapped at the step to create FSheet.
Sub Test()
Dim ws As Worksheet
Dim SheetNames() As String, FSheets() As String, q As String
Dim element As Variant
Dim lastSheet As Integer, r As Integer, incrSheet As Integer, i As Integer
Dim Rgn As Range
' Enter the sheet names into an array. Redim array's size to the number of sheets (lastSheet)
For Each ws In ActiveWorkbook.Worksheets
ReDim Preserve SheetNames(lastSheet)
SheetNames(lastSheet) = ws.name
lastSheet = lastSheet + 1
Next ws
MsgBox lastSheet
' Test condition for each sheet/cell
For z = 4 To 11
For Each element In SheetNames()
incrSheet = 1
If ActiveWorkbook.Sheets(element).Cells(6, z).Value = "S" Then
ReDim Preserve FSheets(incrSheet)
FSheets(incrSheet) = element
incrSheet = incrSheet + 1
End If
Next element
Next z
i = 3
' Define the sheet to work (total project will have more than one, one for code we need test, S, C, etc)
With Worksheets("Diary")
.Activate
.Range("C7").Select
' Concatenate values at Summary page
Do
Cells(7, i).Select
For r = 1 To UBound(FSheets)
'Concatenate with &:
varConctnt = varConctnt & ", " & FSheets(r)
Next r
'remove the "&" before the first element:
varConctnt = Mid(varConctnt, 2)
q = varConctnt
varConctnt = ""
i = i + 1
ActiveCell.Value = q
Loop While i < 11
' Drag the formula for the rest of the rows
Range("C7:J7").Select
Selection.AutoFill Destination:=Range("C7:J12"), Type:=xlFillDefault
End With
End Sub
Where you are going wrong, is your attempt to dynamically set the range. Assuming you are testing the value of a single cell, it is much easier to use Cells, rather than Range, since you can use R1C1 notation. Try something like this:
incrSheet = 1
For z = 4 To 11
For Each element In SheetNames()
If ActiveWorkbook.Sheets(element).Cells(6, z).Value = "S" Then
ReDim Preserve FSheets(incrSheet)
FSheets(incrSheet) = element
MsgBox incrSheet
incrSheet = incrSheet + 1
End If
Next element
Next z

Find Copy and Paste in VBA macro

I am trying to write a macro which search data from one sheet and copy's to another.
But now I have a problem because I want to copy data between two searches and paste the whole data from multiple cells into one single cell.
For example in the above picture my macro:
SEARCH for "--------------" and "*****END OF RECORD"
COPIES everything in between , here example data in row 29 and 30 and from column A,B,C
PASTE all the data from multiple cells A29,B29,C29 and then A30,B30,C30 to single cell in sheet 2 say cell E2.
This pattern is reoccurring in the column A so I want to search for the next occurrence and do all the steps 1,2,3 and this time I will paste it in Sheet2 , cell E3.
Below is the code:
I am able to search my pattern but hard time in giving references to the cells in between those searched patterns and then copying all the data to ONE cell.
x = 2: y = 2: Z = 7000: m = 0: n = 0
Do
x = x + 1
If ThisWorkbook.Sheets("lic").Range("A" & x) = "---------------------" Then m = x
If ThisWorkbook.Sheets("lic").Range("A" & x) = "****** END OF RECORD" Then n = x
If (n > 0) Then
Do
For i = m To n
ThisWorkbook.Sheets("lic").Range("A" & i + 1).Copy
ThisWorkbook.Sheets("lic").Range("B" & i + 1).Copy
ThisWorkbook.Sheets("lic").Range("C" & i + 1).Copy
'If (n > 0) Then ThisWorkbook.Sheets("Sheet1").Range("E" & y) = ThisWorkbook.Sheets("lic").Range("A" & m + 1, "C" & n - 1): y = y + 1
'If (n > 0) Then ThisWorkbook.Sheets("Sheet1").Range("E" & y).Resize(CopyFrom.Rows.Count).Value = CopyFrom.Value: y = y + 1
Loop While Not x > Z
'Driver's Licence #:Driver's Licence #:Driver's Licence #:
x = 2: y = 2: Z = 7000: counter = 1
Do
x = x + 1
If ThisWorkbook.Sheets("lic").Range("A" & x) = "Driver's Licence #:" Then counter = counter + 1
If (counter = 2) Then ThisWorkbook.Sheets("Sheet1").Range("B" & y) = ThisWorkbook.Sheets("lic").Range("C" & x): y = y + 1: counter = 0
If x = Z Then Exit Sub
Loop
End Sub
Considering that the search is working correctly, about the copy thing you just need to do:
Sheet2.Range("E2").value = ThisWorkbook.Sheets("lic").Range("A" & i + 1).value & ";" & ThisWorkbook.Sheets("lic").Range("B" & i + 1).value & ";" & ThisWorkbook.Sheets("lic").Range("C" & i + 1).value
The result will be something like: AIR COO; L DAT; A
--------UPDATE---------
It was hard to understand your code, so I'm write a new one. Basically it's copy what it found on sheet1 to sheet2.
Sub Copy()
Dim count As Integer 'Counter of loops to the for
Dim Z As Integer 'Limit of (?)
Dim h As Integer 'Count the filled cells on sheet2
Dim y As Integer 'Counter the columns to be copied
Z = 7000
h = 1
'Assuming that the "----" will always be on the top, the code will start searching on the second row
'if it's not true, will be needed to validate this to.
For count = 2 To Z
If Sheet1.Cells(count, 1).Value <> "****** END OF RECORD" Then
If Sheet1.Cells(count, 1).Value <> "" Then
For y = 1 To 3 'In case you need to copy more columns just adjust this for.
Sheet2.Cells(h, 1).Value = Sheet2.Cells(h, 1).Value & Sheet1.Cells(count, y).Value
Next y
h = h + 1
End If
Else
MsgBox "END OF RECORD REACHED"
Exit Sub
End If
Next count
End Sub
Maybe I don't get the full idea but this might work for you.
I'm not at all sure what you want to see in the final output, so this is an educated guess:
Sub DenseCopyPasteFill ()
Dim wsFrom, wsTo As Worksheet
Dim ur As Range
Dim row, newRow As Integer
Dim dataOn As Boolean
Dim currentVal As String
dataOn = False
newRow = 3
Set wsFrom = Sheets("Sheet1")
Set wsTo = Sheets("Sheet2")
Set ur = wsFrom.UsedRange
For row = 1 To ur.Rows.Count
If wsFrom.Cells(row, 1).Value2 = "--------------" Then
dataOn = True
ElseIf wsFrom.Cells(row, 1).Value2 = "***** END OF RECORD" Then
newRow = newRow + 1
dataOn = False
ElseIf dataOn Then
currentVal = wsTo.Cells(newRow, 5).Value2
wsTo.Cells(newRow, 5).Value2 = currentVal & _
wsFrom.Cells(row, 1) & wsFrom.Cells(row, 2) & _
wsFrom.Cells(row, 3)
End If
Next row
End Sub
If you can get away without using the Windows clipboard, I would. Instead of copy/paste, here I demonstrated how you can simply add or append a value.
Add this sub:
Sub copy_range(rng As Range)
Dim str As String
str = rng.Cells(1).Value & rng.Cells(2).Value & rng.Cells(3).Value
Range("E" & Range("E" & Rows.Count).End(xlUp).Row + 1).Value = str
End Sub
Then your for loop should look like this:
For i = m To n
copy_range ThisWorkbook.Sheets("lic").Range("A" & i + 1 & ":C" & i + 1)
Next i

Repeat or change cell value based on other cell

I have a macro which fills values in Column G (starting at cell G2) based on user prompts for the start value, then repeats to the end of the column by adding 10 to the value each time.
Sub Macro1()
Dim TagName As String
Dim TagNum As Long, k As Long
' Prompts user for tag names/numbers
TagName = InputBox("What is the product tag name? Ex. APPLE")
TagNum = InputBox("What is the starting tag #? Ex. 10")
' Set values in column G, up to last row in column J
k = 0
With ActiveSheet
LastRow = .Cells(.Rows.Count, "J").End(xlUp).Row
End With
With ActiveSheet.Range("G2")
For i = 1 To LastRow Step 1
.Item(i + 0) = TagName & "_" & TagNum2 + k
k = k + 10
Next
End With
End Sub
This works perfectly if Column C has unique values in each line, like so:
But if the value in Column C repeats, I want the same in Column G, like this:
I think there are couple of errors. Is this what you are trying?
Sub Sample()
Dim TagName As String, sTag As String
Dim TagNum As Long, k As Long
' Prompts user for tag names/numbers
TagName = InputBox("What is the product tag name? Ex. APPLE")
TagNum = InputBox("What is the starting tag #? Ex. 10")
With ActiveSheet
LastRow = .Cells(.Rows.Count, "J").End(xlUp).Row
k = TagNum
.Range("G2").Value = TagName & "_" & k
For i = 3 To LastRow
'~~> Increment k only if the values do not match
If .Range("C" & i).Value <> .Range("C" & i - 1).Value _
Then k = k + 10
.Range("G" & i).Value = TagName & "_" & k
Next
End With
End Sub
I selected "Apple" and "10" in the Input Boxes

Excel VBA Find value in column, and do math

I have
Column A - with Names
Column B - with Quantities
Column C - Where I want returned Value of Quantity x Cost
Column E - with Names but located in different cells
Column F - with Prices
What I'm trying to achieve is: Take value from A1 and find it in E:E (Lets say we found it in E10), when found take value of B1 and multiply it by Respective value of F10 - and put all this in Column C
And so on for all values in column A
I was trying to do it with Do while and two variables x and y, but for some reason it doesn't find all values only for some rows.
Thank you in Advance.
Sub update_button()
'calculates money value for amazon sku
Dim x, y, z As Integer 'x, y, and z variables function as loop counters
'Loop through added SKU/Prices
For x = 4 To 25000
If Worksheets("Sheet1").Range("H" & x) = "" Then
'Blank row found, exit the loop
Exit For
End If
'Loop through Column E to find the first blank row to add the value from H into
For y = 4 To 25000
If Worksheets("Sheet1").Range("E" & y) = "" Then
'Blank row found, Add SKU and Price
Worksheets("Sheet1").Range("E" & y) = Worksheets("Sheet1").Range("H" & x)
Worksheets("Sheet1").Range("F" & y) = Worksheets("Sheet1").Range("I" & x)
'Blank out Columns H and I to prevent need to do it manually
Worksheets("Sheet1").Range("H" & x) = ""
Worksheets("Sheet1").Range("I" & x) = ""
Exit For
End If
Next y
Next x
'---NOW THIS IS WHERE I HAVE THE PROBLEM
'Get Values
Dim intCumulativePrice As Integer
'Loop through report tab and get SKU
x = 4 'initialize x to the first row of data on the Sheet1 tab
Do While Worksheets("Sheet1").Range("A" & x) <> "" 'Loop through valid SKU's to find price of item
y = 4 'initialize y to the first row of SKUs on the Sheet1 tab
Do While Worksheets("Sheet1").Range("E" & y) <> ""
If Worksheets("Sheet1").Range("E" & x) = Worksheets("Sheet1").Range("A" & y) Then 'Check if current SKU on Sheet1 tab matches the current SKU from SKU list
'Calculates the total
intCumulativePrice = intCumulativePrice + (Worksheets("Sheet1").Range("B" & y) * Worksheets("Sheet1").Range("F" & x))
' Puts Quantity X Price in Column B agains every Cell
Worksheets("Sheet1").Range("C" & y) = (Worksheets("Sheet1").Range("B" & y) * Worksheets("Sheet1").Range("F" & x))
Exit Do
End If
y = y + 1
Loop
x = x + 1
Loop
'Puts Grand total in Column L Cell 4
Worksheets("Sheet1").Range("L4") = intCumulativePrice
'Show messagebox to show that report processing has completed
MsgBox "Report processing has been completed successfully", vbInformation, "Processing Complete!"
End Sub
You can do this with a simple VLOOKUP formula in column C.
=VLOOKUP(A1,E1:F65000,2,FALSE)*B1
You can also use a named range for the data in columns E and F, so you don't have to rely on a fixed address like E1:F65000.
To do this with VBA you should copy the source data to Variant arrays and loop over those. Much faster and IMO easier to read and debug.
Something like this
Sub Demo()
Dim Dat As Variant
Dim PriceList As Range
Dim PriceListNames As Variant
Dim PriceListPrices As Variant
Dim Res As Variant
Dim sh As Worksheet
Dim i As Long
Dim nm As String
Dim nmIdx As Variant
Dim FirstDataRow As Long
FirstDataRow = 4
Set sh = ActiveSheet
With sh
Dat = Range(.Cells(FirstDataRow, "B"), .Cells(.Rows.Count, "A").End(xlUp))
Set PriceList = Range(.Cells(FirstDataRow, "E"), .Cells(.Rows.Count, "F").End(xlUp))
PriceListNames = Application.Transpose(PriceList.Columns(1)) ' Need a 1D array for Match
PriceListPrices = PriceList.Columns(2)
ReDim Res(1 To UBound(Dat, 1), 1 To 1)
For i = 1 To UBound(Dat, 1)
nm = Dat(i, 1)
nmIdx = Application.Match(nm, PriceListNames, 0)
If Not IsError(nmIdx) Then
Res(i, 1) = Dat(i, 2) * PriceListPrices(nmIdx, 1)
End If
Next
Range(.Cells(FirstDataRow, 3), .Cells(UBound(Dat, 1) + FirstDataRow - 1, 3)) = Res
End With
End Sub
Try this:
Sub HTH()
With Worksheets("Sheet1")
With .Range("A4", .Range("A" & Rows.Count).End(xlUp)).Offset(, 2)
.Formula = "=VLOOKUP(A4,E:F,2,FALSE)*$B$1"
.Value = .Value
End With
End With
End Sub