I created named ranges in excel, naming of range is based on key values on the sheet2.
Now I created dropdown list on another sheet1, with formula usage - INDIRECT, based again on key, on sheet1 . How I can add blank/special symbol to dropdown list? I cant add empty cell between the sorted items on sheet.
Sheet2:
I have 2 named ranges based on MAT/AE columns, first one is range of C2:C4, and the next one is C5:C6.
And there I have Sheet1, I using data validation , concatenating MAT1&AE11 using formula INDIRECT and i have values based on ranges from Sheet2.
SO my question is, how to add blank/special character to this list?
Code for ranges :
Sub Start()
lf_index_row = 1
lf_name_space_row = 2
gf_namespace = ""
Do
lf_index_row = lf_index_row + 1
lf_material = Sheets(gc_data).Cells(lf_index_row, 1)
lf_location = Sheets(gc_data).Cells(lf_index_row, 2)
gf_new_namespace = "X" & lf_material & lf_location
If gf_new_namespace = "X" Then
If gf_namespace = "" Then
End
Else
'create namespace
Set lf_range = Range(Cells(lf_start_number, 3), Cells(lf_end_number, 3))
lf_range.Select
Range(Cells(lf_start_number, 3), Cells(lf_end_number, 3)).Select
ActiveWorkbook.Names.Add Name:=gf_namespace, RefersTo:=lf_range
End
End If
End If
If gf_namespace <> gf_new_namespace Then
If gf_namespace = "" Then
'initialize newnamespace
gf_namespace = gf_new_namespace
lf_start_number = lf_index_row
lf_end_number = lf_index_row
Else
'create namespace
Set lf_range = Range(Cells(lf_start_number, 3), Cells(lf_end_number, 3))
lf_range.Select
Range(Cells(lf_start_number, 3), Cells(lf_end_number, 3)).Select
ActiveWorkbook.Names.Add Name:=gf_namespace, RefersTo:=lf_range
'initialize newnamespace
gf_namespace = gf_new_namespace
lf_start_number = lf_index_row
lf_end_number = lf_index_row
End If
Else
lf_end_number = lf_index_row
End If
Loop
End Sub
Indirect formula :
Definition of first named range :
If the list is in Range("A1:A10") this is how to achieve a validation list with only one empty position:
with the following code:
Sub TestMe()
Dim list1 As Range
Dim validationFormula As String
Set list1 = Range("A1:A10")
Dim myCell As Range
For Each myCell In list1
If Not IsEmpty(myCell) Then
validationFormula = validationFormula & myCell.Value2 & ","
End If
Next
validationFormula = validationFormula & Chr(160)
With Range("B5").Validation
.Delete
.Add Type:=xlValidateList, Operator:=xlBetween, Formula1:=validationFormula
.IgnoreBlank = False
.InCellDropdown = True
End With
End Sub
What is the idea of the code? The validation string is made in the validationFormula, through concatenating all the cells that are Not IsEmpty(). Once the validationFormula is ready, Chr(160) is added to it, to make sure that we have the empty cell available as well.
It can be added even like this: validationFormula = Chr(160) & "," & validationFormula, if you need to have it at the first position:
Once the validationFormula string is prepared, we can allow ourself to write .IgnoreBlank = True, as far as there is only one blank in the list - the one we need.
Credits to this guy here, for the looping idea - https://superuser.com/questions/1254754/data-validation-from-2-lists-excel-2010
Check if cell with row-value = 3 and column-value = 4 is blank with the following:
Set objExcel = CreateObject("Excel.Application")
Set excelInput = objExcel.Workbooks.Open("myfile")
If excelInput.Sheets("Sheet1").Cells(3, 4) <> vbNullString Then
'do the thing
End If
The above code is VBScript but it should work. If not, its almost identical in VBA.
Related
I need some help to create a macro which adds all the values on the column E between the rows with the "avg" word. the result should be displayed on the cells where the "Sum here" label is displayed. Both texts "avg" and "sum here" is just for illustrate the example, "avg" could be replaced by any other word and "sum here" should actually be the aggregation of the values above it.
The real challenge is that the number of ranges on column E is variable, so i would like to find a macro which is able to deal with "n" number of ranges on column E.
Finally, the values on column D are only the example of the expected value on the "sum here" cells.
This is what I have tried to far:
Sub Macro1()
'
' Macro1 Macro
'
Dim sumhere As Range
Dim startingpoint As Range
Dim endingpoint As Range
'
Range("C17").Select
Selection.End(xlDown).Select
If ActiveCell = "avg" Then
ActiveCell.Offset(rowoffset:=0, columnoffset:=2).Select
Set sumhere = ActiveCell
Set startingpoint = ActiveCell.Offset(rowoffset:=-1, columnoffset:=0)
Selection.End(xlUp).Select
If (ActiveCell.Value) = "Sum here" Then
Set endingpoint = ActiveCell.Offset(rowoffset:=1, columnoffset:=0)
sumhere.Formula = "=sum(range(startingpoint:endingpoint)"
Else
Selection.End(xlUp).Select
If (ActiveCell.Value) = "Sum here" Then
Set endingpoint = ActiveCell.Offset(rowoffset:=1, columnoffset:=0)
sumhere.Formula = "=Sum(Range(startingpoint.adress:endingpoint.adress))"
Else: End If
End If
End If
End Sub
Additionally, as you can see, I do not know, how to define a range using variables. My original idea was to combine this code with some kind of "do while" or/and "for i= 1 to x" and "next i". But I can't see how to combine it.
Using formula only, and providing that column A only has avg (or any text) on each subtotal row.
I've given two versions of the formula - the volatile version (updates everytime you change anything on the spreadsheet), and the non-volatile version (only updates if it needs to).
The formula should be entered on row 6 - change the $E6 to which ever row you need.
(volatile)
=SUM(OFFSET($E6,IFERROR(LOOKUP(2,1/($A$1:INDEX($A:$A,ROW()-1)<>""),ROW($A$1:INDEX($A:$A,ROW()-1))),0)-ROW()+1,,ROW()-1-IFERROR(LOOKUP(2,1/($A$1:INDEX($A:$A,ROW()-1)<>""),ROW($A$1:INDEX($A:$A,ROW()-1))),0)))
(non volatile):
=SUM(INDEX($E:$E,IFERROR(LOOKUP(2,1/($A$1:INDEX($A:$A,ROW()-1)<>""),ROW($A$1:INDEX($A:$A,ROW()-1))),0)+1):INDEX($E:$E,ROW()-1))
or if you don't mind using a helper column:
In cell B6:
=IFERROR(LOOKUP(2,1/($A$1:INDEX($A:$A,ROW()-1)<>""),ROW($A$1:INDEX($A:$A,ROW()-1))),0)
In E6: (volatile)
=SUM(OFFSET($E6,$B6-ROW()+1,,ROW()-1-$B6))
or (non volatile):
=SUM(INDEX($E:$E,$B6):INDEX($E:$E,ROW()-1))
Edit:
Thought I'd add a UDF to calculate it to if you're after VBA.
Use the function =AddSubTotal() in the rows you want the sub total to be shown in, or use =AddSubTotal("pop",6) to sum everything in column F (col 6) using "pop" rather than "avg".
Public Function AddSubTotal(Optional Delim As String = "avg", Optional ColNumber = 5) As Double
Dim rCaller As Range
Dim rPrevious As Range
Dim rSumRange As Range
Set rCaller = Application.Caller
With rCaller.Parent
Set rPrevious = .Range(.Cells(1, 1), .Cells(rCaller.Row - 1, 1)).Find(Delim, , , , , xlPrevious)
If Not rPrevious Is Nothing Then
Set rSumRange = rPrevious.Offset(1, ColNumber - 1).Resize(rCaller.Row - rPrevious.Row - 1)
Else
Set rSumRange = .Range(.Cells(1, ColNumber), .Cells(rCaller.Row - 1, ColNumber))
End If
End With
AddSubTotal = WorksheetFunction.Sum(rSumRange)
End Function
The following VBA routine assumes that
your data is in Columns C:E
Nothing else relevant (nothing numeric) in that range
Your "key word" where you want to show the sum is avg
avg (the key word) is hard-coded in the macro
You could easily modify this routine to also perform an average of those values, and put those results, for example, in Column D
Any of the above are easily modified
Option Explicit
Sub TotalSubRanges()
Dim vSrc As Variant, rSrc As Range
Dim dAdd As Double
Dim I As Long
Const sKey As String = "avg"
Set rSrc = Range(Cells(1, "C"), Cells(Rows.Count, "C").End(xlUp)).Resize(columnsize:=3)
vSrc = rSrc
'Do the "work" in a VBA array, as this will
' execute much faster than working directly
' on the worksheet
For I = 1 To UBound(vSrc, 1)
If vSrc(I, 1) = sKey Then
vSrc(I, 3) = dAdd
dAdd = 0
Else
If IsNumeric(vSrc(I, 3)) Then dAdd = dAdd + vSrc(I, 3)
End If
Next I
'write the results back to the worksheet
' and conditionally format the "sum" cells
With rSrc
.EntireColumn.Clear
.Value = vSrc
.Columns(3).AutoFit
.EntireColumn.ColumnWidth = .Columns(3).ColumnWidth
.FormatConditions.Delete
.FormatConditions.Add _
Type:=xlExpression, _
Formula1:="=" & .Item(1, 1).Address(False, True) & "=""" & sKey & """"
With .FormatConditions(1)
.Interior.ColorIndex = 6
End With
End With
End Sub
Surely you just need something like:
Sub sums()
Dim i As Integer, j As Integer, k As Integer
j = Range("C1048576").End(xlUp).Row
k = 1
For i = 1 To j
If Range("C" & i).Value <> "" Then
Range("E" & i).Value = "=Sum(E" & k & ":E" & i - 1 & ")"
k = i + 1
End If
Next i
End Sub
Change:
Dim startingpoint As Range
Dim endingpoint As Range
To:
Dim startingpoint As Variant
Dim endingpoint As Variant
As the startingpoint and endingpoint is used in a formula, you cant define them as a Range.
I am fairly new to VBA, but I am decently experienced with Java and Python. I was given the task of organizing a nearby school's grading standards on an excel spreadsheet, and I wanted to give the teachers the option to reorganize the spreadsheet at the click of a button, so I have decided to use VBA.
This is what I have so far for my code (it's sloppy, but I will clean it up once I get it working well):
Private Sub Workbook_Open()
' Initialize Variables
Dim i%, j%
Dim vTemp$, StdList$
Dim Stds As Collection
Set Stds = New Collection
' Compute the index of the last Standard on Test worksheet
lastStd = Sheet3.Range("B" & Rows.Count).End(xlUp).Row
' Remove Duplicates from the Standards and remove commas
On Error Resume Next
For i = 2 To lastStd
Stds.Add (Sheet3.Cells(i, 2)), Chr(34) & (Sheet3.Cells(i, 2)) & Chr(34)
Next i
On Error GoTo 0
For i = 1 To Stds.Count
Stds.Item(i) = Replace(Stds.Item(i), ",", Chr(130))
Next i
' Sort the Standards Alphabetically (using Bubble Sort)
For i = 1 To Stds.Count - 1
For j = i + 1 To Stds.Count
If Stds(i) > Stds(j) Then
vTemp = Stds(j)
Stds.Remove (j)
Stds.Add vTemp, vTemp, i
End If
Next j
Next i
' Reinitialize Cell Data
Sheet8.Range("A1:J100").Clear
For i = 1 To Stds.Count
Sheet8.Cells(i, 1).Value = Stds.Item(i)
Next i
' Output the Standards to the Excel Spreadsheet
For i = 1 To Stds.Count
StdList = StdList & Stds.Item(i) & ","
Next i
With ThisWorkbook.Sheets("Sheet1").Range("F3").Validation
.Delete
.Add Type:=xlValidateList, _
AlertStyle:=xlValidateAlertStop, _
Formula1:=StdList
End With
End Sub
The code executes when I open up the spreadsheet, but I get "Run Time Error 1004 'Application Defined or Object Defined Error'" upon execution. The goal is to have the code search through the grading standards, enumerate a collection, remove the duplicates, sort the standards alphabetically, and replace the commas with a character that looks like a comma so that I can convert the collection to a list and place that list into a drop down list somewhere on the spreadsheet. When I select the debug option, these three lines are highlighted:
.Add Type:=xlValidateList, _
AlertStyle:=xlValidateAlertStop, _
Formula1:=StdList
My guess is that I am either struggling with the syntax, or there is a type mismatch somewhere in there that I am not seeing; both of which are likely.
You can run debug.print on your parameters - that would point out that xlValidateAlertStop was empty
It should actually be xlValidAlertStop
Another solution to avoide one loop is to use Scripting.Dictionary object
Sub SubDicList()
'With a CD's Collection DataBase
'|Group And singer |Album Title| Date| Price| Qty | Web Link|
'MainList is DataField of Group and singer
'SubList is The Dropdown Validation List to get from DataField of Album TiTle
Dim ws As Worksheet, nRow As Long, i As Integer
Dim MainList As Range, MainData As Range, DataSelected As Variant
Dim SubList As String, SubData As Variant
Dim Dic As Object
Set Dic = CreateObject("Scripting.Dictionary")
Set ws = Worksheets("Data")
DataSelected = Range("K4") 'Data validation from a dropdown list with MainList Content
nRow = ws.Cells(2 ^ 20, 2).End(xlUp).Row 'Last row
Set MainList = ws.Range(ws.Cells(5, 2), ws.Cells(nRow, 2)) 'MainList as Range (Group and Singers)
For Each MainData In MainList
If MainData.Value = DataSelected Then
SubData = MainData.Offset(0, 1) 'SubData is Album Title of Group and singers
If Not Dic.Exists(SubData) Then 'If Album Title is not in the Sripting Dictionary
Dic.Add SubData, CStr(SubData) 'Add Allbum Titel to the Sripting Dictionary
SubList = SubList & SubData & "," 'Prepare the Validation List for each step of loop
End If
End If
Next MainData
With Range("L4").Validation 'Range("L4"): Where to put the Data Validation Dropdown List
.Delete
.Add Type:=xlValidateList, AlertStyle:=xlValidAlertStop, Operator:= _
xlBetween, Formula1:=SubList
.IgnoreBlank = True
.InCellDropdown = True
.InputTitle = ""
.ErrorTitle = ""
.InputMessage = ""
.ErrorMessage = ""
.ShowInput = True
.ShowError = True
End With
Set Dic = Nothing
Set MainList = Nothing
Set ws = Nothing
End Sub
I need your help please, I have 4 columns in an excel sheet and I need to compare them 2 by 2 i will explain to you :
In column A i have users(user1,user2,user3 ...)
In column B i have functionalities ( fonc1, fonc2, fonc3.....)
In column C i have users(user1,user2,user3 ...)
In column D i have functionalities ( fonc1, fonc2, fonc3.....)
The columns C and D are a new version of columns A and B in the columns C and D the users may change order or change functionalities .
When i execute my code i put the result in other new columns:
column F where i have the users
column G where i put the Deleted_functionalities
column H where i put the New_functionalities
The first problem is that the code doesn't get the users it get only the new and deleted functionalities. The second problem is that when the column A is more than column C where the users are stocked the code doesn't work. Can you please help me to find a solution? Thank you in advance .
Here is my code and the file I am working on :
Private Sub CommandButton2_Click()
Dim rngCell As Range
For Each rngCell In Range("B2:B2000")
If WorksheetFunction.CountIf(Range("D2:D2000"), rngCell) = 0 Then
Range("G" & Rows.Count).End(xlUp).Offset(1) = rngCell
End If
Next
For Each rngCell In Range("D2:D2000")
If WorksheetFunction.CountIf(Range("B2:B2000"), rngCell) = 0 Then
Range("H" & Rows.Count).End(xlUp).Offset(1) = rngCell
End If
Next
End Sub
and this is the excel file
http://www.cjoint.com/c/FCxnwjp22rv
try this
Private Sub CommandButton2_Click()
Dim ws As Worksheet
Dim cell As Range, funcCell As Range
Dim oldUserRng As Range, newUserRng As Range, reportRng As Range
Dim iReport As Long
Dim oldFunc As String, newFunc As String
Set ws = ThisWorkbook.Worksheets("users") '<== adapt it to your needs
With ws
Set oldUserRng = .Columns(1).Resize(.Rows.Count - 1).Offset(1).SpecialCells(xlCellTypeConstants, xlTextValues)
Set newUserRng = .Columns(3).Resize(.Rows.Count - 1).Offset(1).SpecialCells(xlCellTypeConstants, xlTextValues)
Set reportRng = .Range("F1:I1") '<== added one report column to account for unchanged functions
End With
reportRng.Value = Array("user", "deleted", "new", "same")
iReport = 1
For Each cell In oldUserRng
With cell
oldFunc = .Offset(, 1).Value
Set funcCell = FindAndOffset(newUserRng, .Value, 1)
If funcCell Is Nothing Then
reportRng.Offset(iReport) = Array(.Value, "", "", oldFunc)
Else
newFunc = funcCell.Value
If newFunc = oldFunc Then
reportRng.Offset(iReport) = Array(.Value, "", "", newFunc)
Else
reportRng.Offset(iReport) = Array(.Value, oldFunc, newFunc, "")
End If
End If
iReport = iReport + 1
End With
Next cell
For Each cell In newUserRng
With cell
Set funcCell = FindAndOffset(oldUserRng, .Value, 1)
If funcCell Is Nothing Then
reportRng.Offset(iReport) = Array(.Value, "", .Offset(, 1).Value, "")
iReport = iReport + 1
End If
End With
Next cell
End Sub
Not so sure it does what you need.
you'd better provide screenshots of "before" and "after" scenarios.
BTW, is it safe to assume that both old and new user columns cannot hold duplicates (i.e.: two or more "userX" in column A and/or column C?)
But it does speed up thing considerably since it iterates only through non empty cells.
I hope I get what you want to achieve. Does the following solve your problem?
Private Sub CommandButton2_Click()
Dim rngCell As Range
For Each rngCell In Range("A2:A20000")
If WorksheetFunction.CountIf(Range("C2:C20000"), rngCell) > 0 Then
Range("F" & Rows.Count).End(xlUp).Offset(1) = rngCell
Range("F" & Rows.Count).End(xlUp).Offset(0, 1) = rngCell.Offset(0, 1).Value
Range("F" & Rows.Count).End(xlUp).Offset(0, 2) = Application.WorksheetFunction.VLookup(rngCell.Value, Range("C2:D20000"), 2, 0)
ElseIf (rngCell <> "") Then
Range("F" & Rows.Count).End(xlUp).Offset(1) = rngCell
Range("F" & Rows.Count).End(xlUp).Offset(0, 1) = rngCell.Offset(0, 1).Value
End If
Next
For Each rngCell In Range("C2:C20000")
If (WorksheetFunction.CountIf(Range("A2:A20000"), rngCell) = 0 And rngCell <> "") Then
Range("F" & Rows.Count).End(xlUp).Offset(1) = rngCell
Range("F" & Rows.Count).End(xlUp).Offset(0, 2) = rngCell.Offset(0, 1).Value
End If
Next
End Sub
A user is only included in column F when he appears both in columns A and C.In case you want to include every user that is either in column A or C the code has to be altered.
I am trying to automate a spreadsheet to transfer data from one sheet to another sheet depending on what the first 3 characters of the data is. So for example, for the data NDX 12/31/2012 P2600, I would like it to be placed in the NDX sheet. So I have an array (desArr()) that splits that data into different positions of the array, such that desArr(0) contains "NDX", desArr(1) contains "12/31/2012" and so on.
The part I am having trouble with is moving the data to the respective sheets. Specifically, I need a variable reference to these spreadsheets. For instant, take the NDX sheet. I know I can just do NDX.cells(1,1).Paste or Worksheets(NDX.Name).Cells(1,1).Paste and that would work, but what if I want to do that for multiple sheets? I could obviously use If statements to define each different instance, but I wanted to shorten my code. Hence, I am trying to make the reference to the sheet objects variable, i.e. desArr(0).Name, but it returns with an error (which I understand why). Anyone with suggestions on how to achieve this? I know one solution is to just use the name property of the worksheet, but I wanted to avoid the chance of my code failing if someone changed the name of the sheets.
So perhaps like:
Dim desArr() As String, desInfo As String, opType As String
Dim rNum As Long, cNum As Long, i As Long
Dim wb As Workbook
Dim ws As Worksheet
Dim sortRng As Range, findRng As Range
Dim j As Integer 'Throw away after testing
Dim test As String 'Throw away after testing
Dim k As Integer 'Throw away after testing
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Set wb = ThisWorkbook
Set ws = wb.Worksheets(Import.Name)
With ws
rNum = .Range("C1048576").End(xlUp).Row
cNum = 6 'Number of used columns starting from left
Set sortRng = .Range(.Cells(3, 2), .Cells(rNum, cNum))
'Sort range according to Type and Description
sortRng.Sort _
Key1:=.Range("B1"), _
Key2:=.Range("C1")
'Apply conditional formatting
With sortRng.Columns(2)
.FormatConditions.AddUniqueValues
.FormatConditions(.FormatConditions.Count).SetFirstPriority
.FormatConditions(1).DupeUnique = xlDuplicate
With sortRng.Columns(2).FormatConditions(1)
.Interior.PatternColorIndex = xlAutomatic
.Interior.Color = 13551615
.Interior.TintAndShade = 0
.StopIfTrue = False
End With
End With
For i = 0 To (rNum - 2)
With sortRng.Cells(i + 1, 2)
If .DisplayFormat.Interior.Color = "13551615" Then
j = 0
While (.Value = .Offset(j + 1, 0).Value And .Offset(0, 1).Value = .Offset(j + 1, 1).Value)
j = j + 1
Wend
If (j <> 0) Then 'There are duplicates
End If
End If
'Converting the description to format used for classification
If .Offset(0, -1) = "Ext Option" Then
desArr = Split(.Value, " ")
If Not (Left(.Value, 3) = "SX5" Or Left(.Value, 3) = "UKX") Then
'check if it's a call or put
If Left(desArr(3), 1) = "C" Then
opType = "Call"
ElseIf Left(desArr(3), 1) = "P" Then
opType = "Put"
Else
opType = "N/A"
End If
desInfo = Format(desArr(2), "mmmdd") & " " & Right(Trim(desArr(3)), Len(Trim(desArr(3))) - 1) & " " & opType
Else
'check if it's a call or put
If Left(desArr(2), 1) = "C" Then
opType = "Call"
ElseIf Left(desArr(2), 1) = "P" Then
opType = "Put"
Else
opType = "N/A"
End If
desInfo = Format(desArr(1), "mmmdd") & " " & Right(Trim(desArr(2)), Len(Trim(desArr(2))) - 1) & " " & opType
End If
End If
End With
Next i
End With
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
Except that NDX would have to be variable as which worksheet to move the data to depends on the data.
You can use the codename property of the worksheets. If you use NDX.Cells(1,1), NDX is the codename of the sheet. simply search all worksheets, e.g.:
Function GetWorksheet(byval withCodename as String) as Worksheet
Dim sheetVar as Worksheet
For each sheetVar in ThisWorkbook.Worksheets
If sheetVar.CodeName = withCodename Then
Set GetWorksheet = sheetVar
End if
Next
End Function
You could:
Prevent user from renaming sheets
You wrote: "I wanted to avoid the chance of my code failing if someone changed the name of the sheets."
Well, the user can't do this:
If you protect the workbook. You can do this manually in the ribbon (Review > Changes > Protect workbook), or programmatically like this:
ThisWorkbook.Protect 'optionally, add a password -- see documentation for Protect
This will entirely prevent the user from changing sheet names.
I'm trying to print just the values of the yahoo function into the 39th cell with the input being from the first column of the sheet. The function returns a single string. Evaluate, .Value, and .Formula don't workout for me. I'm getting multiple errors and syntax errors. Any input would be much appreciated! I'm a newbie at VBA.
Sub Button2_Click()
Dim LastRow As Long
If Range("A5") <> vbNullString And Range("A6") <> vbNullString Then
LastRow = Range("A5").End(xlDown).Row
End If
With Range("AN5:AN" & LastRow)
Dim texttmp As String: textmp = Evaluate("yahoo(RC[-39])")
'.FormulaR1C1 = "=yahoo(RC[-39])"
'.FormulaR1C1 = "yahoo(MID(RC[-39],1,LEN(RC[-39]))"
'.Value2 = "=yahoo(RC[-39])"
'.Value = "yahoo"
'.Value2 = Evaluate("yahoo(RC[-39])")
End With
End Sub
Use the whole range in one go, fill it with the formula, then overwrite it with the calculated value.
With Range("AN5:AN" & LastRow)
.FormulaR1C1= "=yahoo(RC[-39])"
.Value = .Value
End With