I currently have a cell with some values, separated by commas. I wish to sort them in ascending order.
Sample Input (Value in a single cell):
Sample Output (Value in a single cell):
I have seen many answers when it comes to sorting rows and columns, but I can't seem to sort the values in a single cell in ascending order. Is it possible to sort the values in a single cell in ascending order? Or is there a workaround for this?
Some explanation/documentation would be appreciated as I'm a beginner at VBA. Thank you for your help.
Please try to split data in a single cell by comma, then sorting it and combine all of them together?
The comments provide you with numerous ways to sort an array. Rightly or wrongly, I've provided my own flavour on an old VBA topic along with the extended piece of outputting the string in a delimited format. Take it or leave it ...!
You should be able to refer to the below function directly from any given cell like you would any built-in function in Excel.
Public Function SplitAndSortAscending(ByVal strText As String, ByVal strDelimiter As String) As String
Dim arrData() As String, arrNewData() As String, i As Long, x As Long, y As Long
arrData = Split(strText, strDelimiter)
ReDim arrNewData(UBound(arrData))
For i = 0 To UBound(arrData)
For x = 0 To UBound(arrNewData)
If arrData(i) < arrNewData(x) Or arrNewData(x) = "" Then
For y = UBound(arrNewData) To x + 1 Step -1
arrNewData(y) = arrNewData(y - 1)
Next
arrNewData(x) = arrData(i)
Exit For
End If
Next
Next
For i = 0 To UBound(arrNewData)
SplitAndSortAscending = SplitAndSortAscending & strDelimiter & arrNewData(i)
Next
SplitAndSortAscending = Mid(SplitAndSortAscending, Len(strDelimiter) + 1)
End Function
If you have O365, you can use something like the below to achieve the same sort of thing. Take note, my implementation will take 1.0 and format it as a whole number, i.e. it will come out as 1.
=TEXTJOIN(",",TRUE,SORT(FILTERXML("<r><v>" & SUBSTITUTE(A1,",","</v><v>") & "</v></r>","//v")))
The assumption is that the example you provided is in cell A1.
Hi everyone I hope you're doing fine.
This is my 1st request here in the forum, as I didn't have the chance to find what I'm looking for, I've been searching for this for months but didn't get lucky, and I couldn't figure out how to code it since I'm a beginner.
So basically I have these multiple Excel files that contain columns (long,latitude,altitude, containers,spill, and volume of non contained garbage)
The problem is in the container range, it contains the number of container(num) and the type of container(char) and the volume of the container ( num) separated by "/" character and if we have more than a container in one place we put + sign then number of the second container, type, volume, separated also by "/"
What I'm really looking to do is to sum the number of container and multiply it by the sum of volumes for each cell and then add it to the spill if there is any and then output it in a total column
If there is one type of container then there is no need to do a sum , only multiplication and then add the spill value then output it to the total column
And I'm counting to use "/" sign and + sign as a wildcards to extract the numeric values if this possible
For the volume column there is no math required, only copy paste to the total column.
I apologize for the bad English, you can leave a comment in case it's hard to understand, and I'm looking forward to your help.
Example:
Another example:
You could use a User Defined Function with the code located in a module.
Option Explicit
Function CalcVolume(s As String) As Variant
Dim ar1 As Variant, ar2 As Variant
Dim i As Integer, num As Double, vol As Double, bError As Boolean
bError = False
' split into array
ar1 = Split(s, "+")
For i = LBound(ar1) To UBound(ar1)
ar2 = Split(ar1(i), "/")
If UBound(ar2) = 3 And IsNumeric(ar2(0)) And IsNumeric(ar2(3)) Then
num = num + ar2(0)
vol = vol + ar2(3)
Else
bError = True
End If
Next
If bError = False Then
CalcVolume = vol * num
Else
CalcVolume = CVErr(xlErrNA)
End If
End Function
Sub test()
Debug.Print CalcVolume("2/FUT/p/50+1/FUT/m/100")
End Sub
I have a text type column in excel with these values
2/02/1472
22/88/1234
1/8/1234
22/88/12
01/01/222
88/2222
I want to set a mask that my values do look like this
02/02/1472
22/88/1234
01/08/1234
22/88/1200
01/01/2220
00/88/2222
My mask is 00/00/0000 (if a part does not exist, fill with zero)
I use this "=text(A1,"00/00/0000")" but have error
Since you also mention vba in your tags, here is a User Defined Function:
Option Explicit
Function FormatMask(S As String) As String
Dim V
Dim I As Long
V = Split(S, "/")
V(UBound(V)) = Format(V(UBound(V)), "0000")
For I = UBound(V) - 1 To 0 Step -1
V(I) = Format(V(I), "00")
Next I
FormatMask = Right("00/00/" & Join(V, "/"), 10)
End Function
EDIT
#pnuts pointed out that your examples show that the first two groups are left-padded with 0's, but the third group is right-padded with zero's.
The following modification accomplishes that:
Option Explicit
Function FormatMask(S As String) As String
Dim V
Dim I As Long
V = Split(S, "/")
'This pads with 0's on the left
'V(UBound(V)) = Format(V(UBound(V)), "0000")
'For padding on right as you show for the last group only:
V(UBound(V)) = Left(V(UBound(V)) & "0000", 4)
For I = UBound(V) - 1 To 0 Step -1
V(I) = Format(V(I), "00")
Next I
FormatMask = Right("00/00/" & Join(V, "/"), 10)
End Function
Another example why spreadsheet software is not well suited to text processing, but Excel can manage with a (horrible) formula:
=IF(LEN(IF(LEN(A1)-LEN(SUBSTITUTE(A1,"/",""))=1,"00/"&A1,IF(LEFT(RIGHT(A1,3))="/",A1&"00",IF(LEFT(RIGHT(A1,4))="/",A1&"0",IF(MID(A1,2,1)="/","0"&A1,A1)))))=10,IF(LEN(A1)-LEN(SUBSTITUTE(A1,"/",""))=1,"00/"&A1,IF(LEFT(RIGHT(A1,3))="/",A1&"00",IF(LEFT(RIGHT(A1,4))="/",A1&"0",IF(MID(A1,2,1)="/","0"&A1,A1)))),SUBSTITUTE(IF(LEN(A1)-LEN(SUBSTITUTE(A1,"/",""))=1,"00/"&A1,IF(LEFT(RIGHT(A1,3))="/",A1&"00",IF(LEFT(RIGHT(A1,4))="/",A1&"0",IF(MID(A1,2,1)="/","0"&A1,A1)))),"/","/0",1))
Another horrible formula for you (I've broken it down so I'm using a few helper columns which you can hide so visually it looks the same). Part of the horribleness is also due to your source data not having a consistent format
In Column A I've got your original list which is stored as text (Excel won't recognise these as a date or number)
In Column B to get the first part I have the formula
=IF(LEN(A2)-LEN(SUBSTITUTE(A2,"/",""))=2, VALUE(LEFT(A2,FIND("/",A2)-1)),0)
In Column C to get the middle bit I have
=LEFT(RIGHT(A2,IF(LEN(A2)-LEN(SUBSTITUTE(A2,"/",""))=2,LEN(A2)-FIND("/",A2),LEN(A2))),FIND("/",RIGHT(A2,IF(LEN(A2)-LEN(SUBSTITUTE(A2,"/",""))=2,LEN(A2)-FIND("/",A2),LEN(A2))))-1)
and then in Column D I get the last bit using:
=RIGHT(A2,LEN(A2)-FIND("/",A2,IF(LEN(A2)-LEN(SUBSTITUTE(A2, "/", ""))=2,FIND("/",A2)+1,1)))
I then put it all together in Column E and format it using
=TEXT(B2,"00")&"/"&TEXT(C2, "00") &"/"&TEXT(D2,"0")&REPT(0,4-LEN(D2))
To get your output
You could of course combine this all into one formula, I've just broken it down for a little bit of clearness (although is still pretty bleak):
=TEXT(IF(LEN(A2)-LEN(SUBSTITUTE(A2,"/",""))=2, VALUE(LEFT(A2,FIND("/",A2)-1)),0),"00")&"/"&TEXT(LEFT(RIGHT(A2,IF(LEN(A2)-LEN(SUBSTITUTE(A2,"/",""))=2,LEN(A2)-FIND("/",A2),LEN(A2))),FIND("/",RIGHT(A2,IF(LEN(A2)-LEN(SUBSTITUTE(A2,"/",""))=2,LEN(A2)-FIND("/",A2),LEN(A2))))-1), "00") &"/"&TEXT(RIGHT(A2,LEN(A2)-FIND("/",A2,IF(LEN(A2)-LEN(SUBSTITUTE(A2, "/", ""))=2,FIND("/",A2)+1,1))),"0")&REPT(0,4-LEN(RIGHT(A2,LEN(A2)-FIND("/",A2,IF(LEN(A2)-LEN(SUBSTITUTE(A2, "/", ""))=2,FIND("/",A2)+1,1)))))
A slightly shorter version:
=IF(LEN(A2)-LEN(SUBSTITUTE(A2,"/",""))=1,"00",TEXT(LEFT(A2,FIND("/",A2)-1),"00"))&"/"&TEXT(IFERROR(MID(A2,FIND("/",A2)+1,LOOKUP(99^99,FIND("/",A2,ROW($1:$20)))-FIND("/",A2)-1),TEXT(LEFT(A2,FIND("/",A2)-1),"00")),"00")&"/"&LEFT(RIGHT(A2,LEN(A2)-LOOKUP(99^99,FIND("/",A2,ROW($1:$20))))*10000,4)
The only thing that you want to know is this:
LOOKUP(99^99,FIND("/",A2,ROW($1:$20)))
This is the function to find the last occurrence of /. I assume the maximum length of the string is 20 so you can replace that if needed.
I have a column with formula as follows:
=(2+3*6+8) & "KB"
Basically, each cell is a formula and string concatenated (using &). I want to add all these cells up. I tried the following things:
a) =SUM(B2:B21) gives me a sum of 0.
b) Using =B2+B3... gives me a #VALUE error.
c) I tried something like this also - didn't work, gives a sum of 0: =SUM(IF(ISNUMBER(FIND("KB",$C$2:$C$14)),VALUE(LEFT($C$2:$C$14,FIND("KB",$C$2:$C$14)-1)),0))
Make your own SUM function in VBA. Try this:
=StripTextAndSum(A2:A4) - returns 60
=StripTextAndAverage(A2:A4) - returns 20
This method keeps the left most decimal number and tosses away the rest.
NOTE: You can tweak this to fit your needs. One way would be to retain the text so you can return it in the sum....like 150MB (i am assuming KB means kilobyte). Let me know if you like that idea and I'll make it.
EDIT: As pointed out by #DirkReichel, this has been made a little more efficient using IsNumeric instead, but I have retained all the other functions too. IsLetter is a useful function too.
Public Function StripTextAndSum(myRange As Range)
Dim r As Range
Dim n As Double
n = 0
For Each r In myRange.Cells
n = n + ParseNumberFromString(r.Text)
Next r
StripTextAndSum = n
End Function
Public Function StripTextAndAverage(myRange As Range)
Dim n As Double
n = StripTextAndSum(myRange)
StripTextAndAverage = n / (myRange.Cells.Count * 1#)
End Function
Public Function ParseNumberFromString(s As String) As Double
ParseNumberFromString = Left(s, GetLastNumberIndex(s))
End Function
Public Function GetFirstLetterIndex(s As String) As Integer
Dim i As Integer
For i = 1 To Len(s) Step 1
If IsLetter(Mid(s, i, 1)) = True Then
GetFirstLetterIndex = i
Exit Function
End If
Next i
End Function
Public Function GetLastNumberIndex(s As String) As Integer
Dim i As Integer
For i = Len(s) To 1 Step -1
If IsNumeric(Left(s, i)) = True Then
GetLastNumberIndex = i
Exit Function
End If
Next i
End Function
Function IsLetter(s As String) As Boolean
Dim i As Integer
For i = 1 To Len(s)
If LCase(Mid(s, i, 1)) <> UCase(Mid(s, i, 1)) = True Then
IsLetter = True
Else
IsLetter = False
Exit For
End If
Next
End Function
I'd normally just move the KB to the following column and left-justify it.
That way, it still looks identical but the first column only has real numbers that you can manipulate mathematically to your heart's content.
Or, assuming they're all in kilobytes (which seems to be a requirement if you just want to add the numeric bits), don't put KB in the data area at all.
Instead change the heading from, for example, Used memory to Used memory (KB).
Do you really want to populate your beautiful spreadsheets with butt-ugly monstrosities like the following? :-)
=SUM(IF(ISNUMBER(FIND("KB",$C$2:$C$14)),VALUE(LEFT($C$2:$C$14,FIND("KB",$C$2:$C$14)-1)),0))
If you need to keep your column as-is, you could always use an array formula to get the sum:
=sum(value(left(b2:b21,len(b2:b21)-2)))
You will need to enter this as an array formula (press Ctrl+Shift+Enter to submit it)
Basically this is taking the leftmost chunk of a cell (all but the last two characters, which we know are 'KB'), using value() to convert it into a numeric, and sum() to add it up. Entering it as an array formula just lets us do this to each cell in the list b2:b21 in one swoop.
As #paxdiablo mentioned, though, it might be best to restructure so that you don't have to deal with your values as text in the first place. My approach would be to enter the values and add the "KB" via formatting. You can use a custom formatting with something like 0.00 "KB" so the cell only holds, say, the value 17, but it displays as "17.00 KB".
I'm using LibreOffice Calc to store 16 RGB colors (red in B column, green in C and blue in D), then I want to paint the background of 16 cells with each respective color. Each color is in one row.
At the moment I've been able to achieve this partially: not only 16 cells are painted, but 48 (16x3)! It seems I need to "group" the nested loops. This is my code/macro:
function bgcolor()
Dim Doc, Sheet, CellPaint As Object
Dim CellR, CellG, CellB As String
Doc = ThisComponent
Sheet = Doc.Sheets.getByIndex(0)
For i = 1 to 16 step 1
For j = 1 to 3 step 1
CellR = Sheet.getCellByPosition(j+0,i).getValue()
CellG = Sheet.getCellByPosition(j+1,i).getValue()
CellB = Sheet.getCellByPosition(j+2,i).getValue()
CellPaint = Sheet.getCellByPosition(j+6,i)
CellPaint.CellBackColor = RGB(CellR,CellG,CellB)
next j
next i
bgcolor=CellPaint.CellBackColor
end function
This is the result:
The first column of colors (H) is what I need, exactly. Other two columns appear there, so: how do I fix my nested loops? Probably I need to filter them using a simple if statement, but I'm not sure.
EDIT - For reference: the solution should be similar to this one, however I've read it's not possible on OpenOffice (I assume neither on LibreOffice).
Thanks!
Just drop the inner loop -- You don't need to iterate horizontally:
function bgcolor()
Dim Doc, Sheet, CellPaint As Object
Dim CellR, CellG, CellB As String
Doc = ThisComponent
Sheet = Doc.Sheets.getByIndex(0)
For i = 1 to 16 step 1
CellR = Sheet.getCellByPosition(1,i).getValue()
CellG = Sheet.getCellByPosition(2,i).getValue()
CellB = Sheet.getCellByPosition(3,i).getValue()
CellPaint = Sheet.getCellByPosition(7,i)
CellPaint.CellBackColor = RGB(CellR,CellG,CellB)
next i
bgcolor=CellPaint.CellBackColor
end function