I would like to know if there is a way to get a random letter (from A-Z)
Thanks for any help.
I think this is what you're looking for. Generate a Random Letter in ASP:
Function RandomNumber(LowNumber, HighNumber)
RANDOMIZE
RandomNumber = Round((HighNumber - LowNumber + 1) * Rnd + LowNumber)
End Function
Assign the function to a variable and pass in the LowNumber (26) and
the HighNumber (97) and convert the value returned to the character it
represents:
RandomLetter = CHR(RandomNumber(97,122))
You'll want your range to be between 65 and 90 (A and Z) for capital letters.
Roger Baretto's answer fixed with Cem's hint ))
Function RandomString(iSize)
Const VALID_TEXT = "abcdefghijklmnopqrstuvwxyz1234567890"
Dim Length, sNewSearchTag, I
Length = Len(VALID_TEXT)
Randomize()
For I = 1 To iSize
sNewSearchTag = sNewSearchTag & Mid(VALID_TEXT, Int(Rnd()*Length + 1), 1)
Next
RandomString = sNewSearchTag
End Function
Here is another way to look at it without using an if/switch.
String alphabet = "abcdefghijklmnopqrstuvwxyz";
Random rand = new Random();
char randomCharacter = alphabet[rand.Next(0, 25)];
I came to a solution that you can have easy control of what are the valid values for your generator.
Function CreateRandomString(iSize)
Const VALID_TEXT = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
Dim sNewSearchTag
Dim I
For I = 0 To iSize
Randomize
sNewSearchTag = sNewSearchTag & Mid(VALID_TEXT,Round(Rnd * Len(VALID_TEXT)),1)
Next
CreateRandomString = sNewSearchTag
End Function
use a random number... like this:
Function RandomNumber(LowNumber, HighNumber)
RANDOMIZE
RandomNumber = Round((HighNumber - LowNumber + 1) * Rnd + LowNumber)
End Function
and then use it from 1-26, use "if" or switch, to get the letter.
Rogerio's answer is fine but Round(Rnd * Len(VALID_TEXT)) can be 0 and Mid cannot start from 0. Fix it if you want to use this function.
Related
I want to take a number and convert it into lowercase a-z letters using VBScript.
For example:
1 converts to a
2 converts to b
27 converts to aa
28 converts to ab
and so on...
In particular I am having trouble converting numbers after 26 when converting to 2 letter cell names. (aa, ab, ac, etc.)
You should have a look at the Chr(n) function.
This would fit your needs from a to z:
wscript.echo Chr(number+96)
To represent multiple letters for numbers, (like excel would do it) you'll have to check your number for ranges and use the Mod operator for modulo.
EDIT:
There is a fast food Copy&Paste example on the web: How to convert Excel column numbers into alphabetical characters
Quoted example from microsoft:
For example: The column number is 30.
The column number is divided by 27: 30 / 27 = 1.1111, rounded down by the Int function to "1".
i = 1
Next Column number - (i * 26) = 30 -(1 * 26) = 30 - 26 = 4.
j = 4
Convert the values to alphabetical characters separately,
i = 1 = "A"
j = 4 = "D"
Combined together, they form the column designator "AD".
And its code:
Function ConvertToLetter(iCol As Integer) As String
Dim iAlpha As Integer
Dim iRemainder As Integer
iAlpha = Int(iCol / 27)
iRemainder = iCol - (iAlpha * 26)
If iAlpha > 0 Then
ConvertToLetter = Chr(iAlpha + 64)
End If
If iRemainder > 0 Then
ConvertToLetter = ConvertToLetter & Chr(iRemainder + 64)
End If
End Function
Neither of the solutions above work for the full Excel range from A to XFD. The first example only works up to ZZ. The second example has boundry problems explained in the code comments below.
//
Function ColumnNumberToLetter(ColumnNumber As Integer) As String
' convert a column number to the Excel letter representation
Dim Div As Double
Dim iMostSignificant As Integer
Dim iLeastSignificant As Integer
Dim Base As Integer
Base = 26
' Column letters are base 26 starting at A=1 and ending at Z=26
' For base 26 math to work we need to adjust the input value to
' base 26 starting at 0
Div = (ColumnNumber - 1) / Base
iMostSignificant = Int(Div)
' The addition of 1 is needed to restore the 0 to 25 result value to
' align with A to Z
iLeastSignificant = 1 + (Div - iMostSignificant) * Base
' convert number to letter
ColumnNumberToLetter = Chr(64 + iLeastSignificant)
' if the input number is larger than the base then the conversion we
' just did is the least significant letter
' Call the function again with the remaining most significant letters
If ColumnNumber > Base Then
ColumnNumberToLetter = ColumnNumberToLetter(iMostSignificant) & ColumnNumberToLetter
End If
End Function
//
try this
function converts(n)
Dim i, c, m
i = n
c = ""
While i > 26
m = (i mod 26)
c = Chr(m+96) & c
i = (i - m) / 26
Wend
c = Chr(i+96) & c
converts = c
end function
WScript.Echo converts(1000)
Software: MS Visual Studio Shell 2013
I am currently working on a matrix heat map for an SSRS Report that I am trying to publish.
The textbox I am trying to apply the background to is grouped in by a row and column. It has a count function to determine the value of that cell.
Report Code I am using is:
public const ColorScaleRed = "#FF0000"
public const ColorScalePink= "#ff6666"
public function ColorScaleWPR(value, minValue, maxValue) as string
ColorScaleWPR = ColorScale3(value, _
minValue, "White", _
ColorScalePink, _
maxValue, ColorScaleRed)
end function
public function ColorScale3(value as object, minValue as object, minColor as string, midColor as string, maxValue as object, maxColor as string) as string
' Use average of minValue and maxValue as midValue
dim midValue as object
if IsNumeric(minValue) and IsNumeric(maxValue) then
midValue = (CDbl(minValue) + CDbl(maxValue)) / 2
end if
ColorScale3 = ColorScale3(value, _
minValue, minColor, _
midValue, midColor, _
maxValue, maxColor)
end function
public function ColorScale3(value as object, minValue as object, minColor as string, midValue as object, midColor as string, maxValue as object, maxColor as string) as string
if IsNumeric(value) and IsNumeric(midValue) and CDbl(value) < CDbl(midValue) then
ColorScale3 = ColorScale(value, minValue, minColor, midValue, midColor)
else
ColorScale3 = ColorScale(value, midValue, midColor, maxValue, maxColor)
end if
end function
public function ColorScale(value as object, minValue as object, minColor as string, maxValue as object, maxColor as string, optional errorColor as string = "Transparent") as string
ColorScale = errorColor
if not IsNumeric(value) or not IsNumeric(minValue) or not IsNumeric(maxValue) then
exit function
end if
' Do all calculations using doubles (can't mix doubles and decimals)
value = CDbl(value)
minValue = CDbl(minValue)
maxValue = CDbl(maxValue)
if minValue >= maxValue then
exit function
end if
if value <= minValue then
ColorScale = minColor
exit function
end if
if value >= maxValue then
ColorScale = maxColor
exit function
end if
dim scaleValue, r, g, b as double
dim minRGB, minR, minG, minB as integer
dim maxRGB, maxR, maxG, maxB as integer
scaleValue = (value - minValue) / (maxValue - minValue)
minRGB = GetRGB(minColor)
minR = minRGB / 2^16
minG = (minRGB mod 2^16) / 2^8
minB = minRGB mod 2^8
maxRGB = GetRGB(maxColor)
maxR = maxRGB / 2^16
maxG = (maxRGB mod 2^16) / 2^8
maxB = maxRGB mod 2^8
r = minR + ((maxR - minR) * scaleValue)
g = minG + ((maxG - minG) * scaleValue)
b = minB + ((maxB - minB) * scaleValue)
ColorScale = string.Format("#{0:X2}{1:X2}{2:X2}", _
CInt(Math.Floor(r)), _
CInt(Math.Floor(g)), _
CInt(Math.Floor(b)))
end function
private function GetRGB(colorStr as string) as integer
GetRGB = 0
if colorStr.StartsWith("#") then
GetRGB = Int32.Parse(colorStr.Substring(1), System.Globalization.NumberStyles.AllowHexSpecifier)
exit function
end if
dim c as System.Drawing.Color
c = System.Drawing.Color.FromName(colorStr)
GetRGB = (c.R * 2^16) + (c.G * 2^8) + c.B
end function
My problem is when i set the background expression.
I am able to get the heat map to properly render using the following expression:
=Code.ColorScaleWPR(Count(Fields!Candidate_ID.Value),0,10)
However, I don't want to use static values for the min and max values. I want them to be dynamic because depending on the parameters chosen those min and max values could vary widely.
So I used the following code
=Code.ColorScaleWPR(Count(Fields!Candidate_ID.Value), Min(Fields!Candidate_ID.Value,"DataSet1"), Max(Fields!Candidate_ID.Value,"DataSet1"))
When I run the report it renders completely white and none of the conditioning is applied to the cells.
Any suggestions to make it dynamic would be awesome.
Thanks!
I do not think that RS is able to calculate all groupings upfront then find min/max from them to apply color scale.
The best approach to try, is to calculate min and max values for every group into separate Dataset from the original Dataset query. Then pull needed values based on matching group criteria using expression. I would go that direction...
PS. I did not have enough time to make that beast alive for my case when I am using color scale on pivoted matrix groupings:)
Looking for the best way to do this in VB6. Typically, I would use this approach...
' count spaces
For i = 1 To Len(text)
If Mid$(text, i, 1) = " " Then count = count + 1
Next
Not saying it's the best way, but you code do:
distinctChr = " "
count = Len(text) - Len(Replace(text, distinctChr , ""))
Use the split command like this
Dim TempS As String
TempS = " This is a split test "
Dim V As Variant
V = Split(TempS, " ")
Cls
Print UBound(V) '7
V = Split(TempS, "i")
Print UBound(V) '3
V = Split(TempS, "e")
Print UBound(V) '1
You can combine it to a single line.
Print UBound(Split(TempS, "i"))
I did some crude timing on it. On a 40,000 character string with all spaces it seems to clock in at 17 milliseconds on a 2.4 GHz Intel Core 2 processor.
A function could look like this
Function CountChar(ByVal Text As String, ByVal Char As String) As Long
Dim V As Variant
V = Split(Text, Char)
CountChar = UBound(V)
End Function
I would use a modified bucket sort:
Dim i as Integer
Dim index As Integer
Dim count as Integer
Dim FoundByAscii(0 To 255) As Boolean
For i = 1 To Len(text)
index = Asc(Mid$(text, i, 1))
FoundByAscii(index) = True
Next i
count = 0
For i = 0 To 255
If FoundByAscii(i) Then
count = count + 1
End If
Next i
...and your result is in count. The performance is O(N) - if Mid$ is O(1).
Edit:
Based on your clarification, do this:
' count spaces
Dim asciiToSearchFor As Integer
asciiToSearchFor = Asc(" ")
For i = 1 To Len(text)
If Asc(Mid$(text, i, 1)) = asciiToSearchFor Then count = count + 1
Next
As ascii compares have to be faster that string comparison. I'd profile it just in case, but I'm pretty sure.
It's not clear what you mean by the best way to do this.
If you want something very fast, but totally unmaintainable, adapt this horrible code that delves into the underlying memory of a VB6 string to count the number of words. Courtesy of VBspeed.
What's the best way to test two Singles for equality in VB6?
I want to test two Single values for equality to 7 significant figures.
This MSDN article recommends using something like
If Abs(a - b) <= Abs(a / 10 ^ 7) Then
valuesEqual = True
End If
However, that can fail for certain values, e.g.
Public Sub Main()
Dim a As Single
Dim b As Single
a = 0.50000005
b = 0.50000014
Debug.Print "a = " & a
Debug.Print "b = " & b
Debug.Print "a = b: " & (a = b)
Debug.Print "SinglesAreEqual(a, b): " & SinglesAreEqual(a, b)
// Output:
// a = 0.5000001
// b = 0.5000001
// b = b: False
// SinglesAreEqual(a, b): False
End Sub
Private Function SinglesAreEqual(a As Single, b As Single) As Boolean
If Abs(a - b) <= Abs(a / 10 ^ 7) Then
SinglesAreEqual = True
Else
SinglesAreEqual = False
End If
End Function
The simplest way I've found of getting the result I need is to convert the values to strings, but seems horribly ugly:
Private Function SinglesAreEqual(a As Single, b As Single) As Boolean
SinglesAreEqual = (Str$(a) = Str$(b))
End Function
Are there any better ways?
I maintain a CAD/CAM application and I have to deal with floating point numbers all the time. I have a function that I call fComp that I pass a floating point value when I need to test for equality. fComp call a rounding function set to a certain level of precision. For our system I round to 6 decimal places. Yours may need higher or get away with lower it depends on the application.
The fComp Function exists so I have one spot to change the rounding factor used in these calculations. This proved handy a couple of years back when we started manufacturing higher precision machines.
Public Function pRound(ByVal Value As Double, ByVal Power As Double) As Double
Dim TempValue As Double
Dim tSign As Double
TempValue = Value
tSign = TempValue
TempValue = Abs(TempValue)
TempValue = TempValue * 10 ^ (Power * -1)
TempValue = Fix(TempValue + 0.5)
TempValue = TempValue / 10 ^ (Power * -1)
pRound = TempValue * Sign(tSign)
End Function
To round to the 6th decimal place you go
RoundedNumber = pRound(MyValue, -6)
Negative is to the right of the decimal place positive to the left.
Instead if rounding and testing for equality, you can take the difference of two numbers and compare that with a factor
If Abs(a - b) < 0.000001 Then
You can adjust the 0.000001 to whatever resolution you need
I don't believe you can use the single data type to that many significant figures. You would need to use double instead:
Dim a As Single
Dim s As String
s = "0.50000005"
a = 0.50000005
Debug.Print s & " " & a
The above outputs:
0.50000005
0.5000001
This question already has answers here:
How to convert a column number (e.g. 127) into an Excel column (e.g. AA)
(60 answers)
Closed 9 years ago.
How would you determine the column name (e.g. "AQ" or "BH") of the nth column in Excel?
Edit: A language-agnostic algorithm to determine this is the main goal here.
I once wrote this function to perform that exact task:
public static string Column(int column)
{
column--;
if (column >= 0 && column < 26)
return ((char)('A' + column)).ToString();
else if (column > 25)
return Column(column / 26) + Column(column % 26 + 1);
else
throw new Exception("Invalid Column #" + (column + 1).ToString());
}
Here is the cleanest correct solution I could come up with (in Java, but feel free to use your favorite language):
String getNthColumnName(int n) {
String name = "";
while (n > 0) {
n--;
name = (char)('A' + n%26) + name;
n /= 26;
}
return name;
}
But please do let me know of if you find a mistake in this code, thank you.
A language agnostic algorithm would be as follows:
function getNthColumnName(int n) {
let curPower = 1
while curPower < n {
set curPower = curPower * 26
}
let result = ""
while n > 0 {
let temp = n / curPower
let result = result + char(temp)
set n = n - (curPower * temp)
set curPower = curPower / 26
}
return result
This algorithm also takes into account if Excel gets upgraded again to handle more than 16k columns. If you really wanted to go overboard, you could pass in an additional value and replace the instances of 26 with another number to accomodate alternate alphabets
Thanks, Joseph Sturtevant! Your code works perfectly - I needed it in vbscript, so figured I'd share my version:
Function ColumnLetter(ByVal intColumnNumber)
Dim sResult
intColumnNumber = intColumnNumber - 1
If (intColumnNumber >= 0 And intColumnNumber < 26) Then
sResult = Chr(65 + intColumnNumber)
ElseIf (intColumnNumber >= 26) Then
sResult = ColumnLetter(CLng(intColumnNumber \ 26)) _
& ColumnLetter(CLng(intColumnNumber Mod 26 + 1))
Else
err.Raise 8, "Column()", "Invalid Column #" & CStr(intColumnNumber + 1)
End If
ColumnLetter = sResult
End Function
Joseph's code is good but, if you don't want or need to use a VBA function, try this.
Assuming that the value of n is in cell A2
Use this function:
=MID(ADDRESS(1,A2),2,LEN(ADDRESS(1,A2))-3)
IF(COLUMN()>=26,CHAR(ROUND(COLUMN()/26,1)+64)&CHAR(MOD(COLUMN(),26)+64),CHAR(COLUMN()+64))
This works 2 letter columns (up until column ZZ). You'd have to nest another if statement for 3 letter columns.
The formula above fails on columns AY, AZ and each of the following nY and nZ columns. The corrected formula is:
=IF(COLUMN()>26,CHAR(ROUNDDOWN((COLUMN()-1)/26,0)+64)&CHAR(MOD((COLUMN()-1),26)+65),CHAR(COLUMN()+64)
Ruby one-liner:
def column_name_for(some_int)
some_int.to_s(26).split('').map {|c| (c.to_i(26) + 64).chr }.join # 703 => "AAA"
end
It converts the integer to base26 then splits it and does some math to convert each character from ascii. Finally joins 'em all back together. No division, modulus, or recursion.
Fun.
FROM wcm:
If you don't want to use VBA, you can use this
replace colnr with the number you want
=MID(ADDRESS(1,colnr),2,LEN(ADDRESS(1,colnr))-3)
Please be aware of the fact that this formula is volatile because of the usage of the ADDRESS function. Volatile functions are functions that are recalculated by excel after EVERY change.
Normally excel recalculates formula's only when their dependent references changes.
It could be a performance killer, to use this formula.
And here is a conversion from the VBScript version to SQL Server 2000+.
CREATE FUNCTION [dbo].[GetExcelColRef]
(
#col_seq_no int
)
RETURNS varchar(5)
AS
BEGIN
declare #Result varchar(5)
set #Result = ''
set #col_seq_no = #col_seq_no - 1
If (#col_seq_no >= 0 And #col_seq_no < 26)
BEGIN
set #Result = char(65 + #col_seq_no)
END
ELSE
BEGIN
set #Result = [dbo].[GetExcelColRef] (#col_seq_no / 26) + '' + [dbo].[GetExcelColRef] ((#col_seq_no % 26) + 1)
END
Return #Result
END
GO
This works fine in MS Excel 2003-2010. Should work for previous versions supporting the Cells(...).Address function:
For the 28th column - taking columnNumber=28; Cells(1, columnNumber).Address returns "$AB$1".
Doing a split on the $ sign returns the array: ["","AB","1"]
So Split(Cells(1, columnNumber).Address, "$")(1) gives you the column name "AB".
UPDATE:
Taken from How to convert Excel column numbers into alphabetical characters
' The following VBA function is just one way to convert column number
' values into their equivalent alphabetical characters:
Function ConvertToLetter(iCol As Integer) As String
Dim iAlpha As Integer
Dim iRemainder As Integer
iAlpha = Int(iCol / 27)
iRemainder = iCol - (iAlpha * 26)
If iAlpha > 0 Then
ConvertToLetter = Chr(iAlpha + 64)
End If
If iRemainder > 0 Then
ConvertToLetter = ConvertToLetter & Chr(iRemainder + 64)
End If
End Function
APPLIES TO: Microsoft Office Excel 2007 SE / 2002 SE / 2000 SE / 97 SE
I suppose you need VBA code:
Public Function GetColumnAddress(nCol As Integer) As String
Dim r As Range
Set r = Range("A1").Columns(nCol)
GetColumnAddress = r.Address
End Function
This does what you want in VBA
Function GetNthExcelColName(n As Integer) As String
Dim s As String
s = Cells(1, n).Address
GetNthExcelColName = Mid(s, 2, InStr(2, s, "$") - 2)
End Function
This seems to work in vb.net
Public Function Column(ByVal pColumn As Integer) As String
pColumn -= 1
If pColumn >= 0 AndAlso pColumn < 26 Then
Return ChrW(Asc("A"c) + pColumn).ToString
ElseIf (pColumn > 25) Then
Return Column(CInt(math.Floor(pColumn / 26))) + Column((pColumn Mod 26) + 1)
Else
stop
Throw New ArgumentException("Invalid column #" + (pColumn + 1).ToString)
End If
End Function
I took Joseph's and tested it to BH, then fed it 980-1000 and it looked good.
In VBA, assuming lCol is the column number:
function ColNum2Letter(lCol as long) as string
ColNum2Letter = Split(Cells(1, lCol).Address, "$")(0)
end function
All these code samples that these good people have posted look fine.
There is one thing to be aware of. Starting with Office 2007, Excel actually has up to 16,384 columns. That translates to XFD (the old max of 256 colums was IV). You will have to modify these methods somewhat to make them work for three characters.
Shouldn't be that hard...
Here's Gary Waters solution
Function ConvertNumberToColumnLetter2(ByVal colNum As Long) As String
Dim i As Long, x As Long
For i = 6 To 0 Step -1
x = (1 - 26 ^ (i + 1)) / (-25) - 1 ‘ Geometric Series formula
If colNum > x Then
ConvertNumberToColumnLetter2 = ConvertNumberToColumnLetter2 & Chr(((colNum - x - 1)\ 26 ^ i) Mod 26 + 65)
End If
Next i
End Function
via http://www.dailydoseofexcel.com/archives/2004/05/21/column-numbers-to-letters/
Considering the comment of wcm (top value = xfd), you can calculate it like this;
function IntToExcel(n: Integer); string;
begin
Result := '';
for i := 2 down to 0 do
begin
if ((n div 26^i)) > 0) or (i = 0) then
Result := Result + Char(Ord('A')+(n div (26^i)) - IIF(i>0;1;0));
n := n mod (26^i);
end;
end;
There are 26 characters in the alphabet and we have a number system just like hex or binary, just with an unusual character set (A..Z), representing positionally the powers of 26: (26^2)(26^1)(26^0).
FYI T-SQL to give the Excel column name given an ordinal (zero-based), as a single statement.
Anything below 0 or above 16,383 (max columns in Excel2010) returns NULL.
; WITH TestData AS ( -- Major change points
SELECT -1 AS FieldOrdinal
UNION ALL
SELECT 0
UNION ALL
SELECT 25
UNION ALL
SELECT 26
UNION ALL
SELECT 701
UNION ALL
SELECT 702
UNION ALL
SELECT 703
UNION ALL
SELECT 16383
UNION ALL
SELECT 16384
)
SELECT
FieldOrdinal
, CASE
WHEN FieldOrdinal < 0 THEN NULL
WHEN FieldOrdinal < 26 THEN ''
WHEN FieldOrdinal < 702 THEN CHAR (65 + FieldOrdinal / 26 - 1)
WHEN FieldOrdinal < 16384 THEN CHAR (65 + FieldOrdinal / 676 - 1)
+ CHAR (65 + (FieldOrdinal / 26) - (FieldOrdinal / 676) * 26 - 1)
ELSE NULL
END
+ CHAR (65 + FieldOrdinal % 26)
FROM TestData
ORDER BY FieldOrdinal
I currently use this, but I have a feeling that it can be optimized.
private String GetNthExcelColName(int n)
{
String firstLetter = "";
//if number is under 26, it has a single letter name
// otherwise, it is 'A' for 27-52, 'B' for 53-78, etc
if(n > 26)
{
//the Converts to double and back to int are just so Floor() can be used
Double value = Convert.ToDouble((n-1) / 26);
int firstLetterVal = Convert.ToInt32(Math.Floor(value))-1;
firstLetter = Convert.ToChar(firstLetterValue + 65).ToString();
}
//second letter repeats
int secondLetterValue = (n-1) % 26;
String secondLetter = Convert.ToChar(secondLetterValue+65).ToString();
return firstLetter + secondLetter;
}
=CHAR(64+COLUMN())