I am importing a csv-file containing a data column with semicolon separated bytes in hexadecimal format like this:
06;03;58;1C;05;F5;D2;70;05;F5;DF;...
(Yes, this is all one column in the comma separated file..)
I would like to parse this column into a number of columns with 32-bit values and convert them to decimal:
06;03;58;1C -> 0x0603581C -> 100882460
05;F5;D2;70 -> 0x05F5D270 -> 99996272 ...
Here is one of my first (futile) attempts to create the first column:
Integer(Concatenate("0x",
Mid([data], 1, 2),
Mid([data], 4, 2),
Mid([data], 7, 2),
Mid([data], 10, 2)))
Any suggestions on how to accomplish this?
I am trying to avoid the extra step of pre-processing the csv-file in Excel using this very similar calculation:
HEX2DEC(CONCATENATE(
MID($M2,1,2),
MID($M2,4,2),
MID($M2,7,2),
MID($M2,10,2)))
The easiest way is probably to use either IronPython or the R interface. However, here's a version using just calculated columns (quite ugly, but gets the job done):
extract the one-character substring for pos 1, 2, ...
replace each character with its numeric value, i.e. 'A' -> '10', 'B' -> '11', ...
convert it to an integer Int1, Int2, ...
compute the resulting value as ((Int1*16) + Int2)*16 + ...
Here are the column expressions for the calculated columns (I only did the first two characters):
Int1
Integer(
Substitute(
Substitute(
Substitute(
Substitute(
Substitute(
Substitute(
Mid([Input],1,1),
"A","10"),
"B","11"),
"C","12"),
"D","13"),
"E","14"),
"F","15"))
Int2
Integer(
Substitute(
Substitute(
Substitute(
Substitute(
Substitute(
Substitute(
Mid([Input],2,1),
"A","10"),
"B","11"),
"C","12"),
"D","13"),
"E","14"),
"F","15"))
Result
Integer(([Int1]*16) + [Int2])
Related
In column A, starting with A1, I have a set of database column names which are Pascale case and without spaces. I'd like to use an Excel formula in column B to insert spaces before each Capital letter or number. Ideally any consecutive capital letters or numbers would remain together. I've done this in the past with C#, but on this project, I can't even use VBA macros. Example output:
Can this, or something close, be achieved using only formulas?
This is pretty hard, but with ms365 doable with the give sample data:
Formula in B1:
=MAP(A1:A10,LAMBDA(v,TRIM(REDUCE(v,SEQUENCE(LEN(v),,LEN(v),-1),LAMBDA(a,b,LET(x,MAKEARRAY(26,3,LAMBDA(r,c,CHOOSE(c,CHAR(r+64),CHAR(r+96),r-0))),y,MID(a,b,1),z,MID(a,b+1,1),r,BYCOL(x,LAMBDA(c,SUM(EXACT(c,y)+EXACT(c,z)))),IF(MAX(r)=1,LEFT(a,b-1)&IF((CONCAT(r)="110")*(EXACT(UPPER(y),y))," "&y,y&" ")&RIGHT(a,LEN(a)-b),a)))))))
Maybe others have shorter solutions...
Just for fun, this uses a single Reduce but I have defined some auxiliary functions. I put them in a module called 'is' in the Advanced Formula Environment so their full names are Is.Upper, Is.Lower and Is.Digit:
Upper=lambda(c,if(c="",false,and(code(c)>64,code(c)<91)));
Digit=lambda(c,if(c="",false,and(code(c)>47,code(c)<58)));
Lower=lambda(c,if(c="",false,and(code(c)>96,code(c<123))))
=REDUCE(LEFT(A1,1),SEQUENCE(1,LEN(A1)-1,2),LAMBDA(a,c,a&IF(OR(AND(is.Digit(MID(A1,c,1)),NOT(is.Digit(MID(A1,c-1,1)))),AND(is.Upper(MID(A1,c,1)),OR(NOT(is.Upper(MID(A1,c-1,1))),is.Lower(MID(A1,c+1,1)))))," ","")&MID(A1,c,1)))
This is how the main formula looks in the Advanced Formula Environment:
=REDUCE(
LEFT(A2, 1),
SEQUENCE(1, LEN(A2) - 1, 2),
LAMBDA(a, c,
a &
IF(
OR(
AND(
is.Digit(MID(A2, c, 1)),
NOT(is.Digit(MID(A2, c - 1, 1)))
),
AND(
is.Upper(MID(A2, c, 1)),
OR(
NOT(is.Upper(MID(A2, c - 1, 1))),
is.Lower(MID(A2, c + 1, 1))
)
)
),
" ",
""
) & MID(A2, c, 1)
)
)
Note - assumes string length>1.
I got some data in Excel and most of them are in the format of hh:mm:ss. So I can use the formula like "=HOUR(A2)*3600 + MINUTE(A2)*60 + SECOND(A2)" to convert it.
But this does not work for the dd:hh:mm:ss format, since day() function does not work like hour()/minute()/second() here. Could anyone help me?
In Excel, time is expressed as days and fractions of a day.
There are 86400 seconds in one day.
It appears that your data is all text.
Text strings that include the dd parameter will not convert automatically; without that, it will as the result any mathematical operation.
So you can test for the error to treat the dd formatted strings differently
Multiply the value by 86400 => number of seconds
If there is an error, use LEFT to extract the dd portion; MID to extract the time portion; then add them together and multiply the summation by 86400.
Try:
=IFERROR(A14*86400,(LEFT(A14,FIND(":",A14)-1)+ MID(A14,FIND(":",A14)+1,99))*86400)
(If you want whole seconds, you can wrap the formula in a ROUND or INT function)
=IF(LEN(A2)-LEN(SUBSTITUTE(A2;":";""))=3;DAY(LEFT(A2;SEARCH(":";A2)-1))*24*3600+HOUR(RIGHT(A2;LEN(A2)-SEARCH(":";A2)))*3600+MINUTE(RIGHT(A2;LEN(A2)-SEARCH(":";A2)))*60+SECOND(RIGHT(A2;LEN(A2)-SEARCH(":";A2)));HOUR(A2)*3600+MINUTE(A2)*60+SECOND(A2))
OPTION WITH ALL DECIMALS: When you use SECOND, you are ignoring all decimals, that's why values like 12.9883 are being rounded to 13 seconds. If you need all decimals, you can do:
=IF(LEN(A1)-LEN(SUBSTITUTE(A1;":";""))=3;DAY(LEFT(A1;SEARCH(":";A1)-1))*24*3600+VALUE(RIGHT(A1;LEN(A1)-SEARCH(":";A1)))*24*60*60;VALUE(A1)*24*60*60)
And your output will be this:
As the strings represent time use the VALUE function to convert them to a time serial number, after that apply the
TEXT function to convert the time serial to seconds.
Also use the Left, Right and Substitute functions to separate the days, time and fraction of seconds.
This formula returns the total of seconds represented in column A
= IFERROR( --TEXT( VALUE($A1), "[ss]" ),
--TEXT( --LEFT( SUBSTITUTE( $A1, ":", REPT( " ", 25 ), 1 ), 25 )
--TRIM( RIGHT( SUBSTITUTE( $A1, ":", REPT( " ", 25 ), 1 ), 25 ) ), "[ss]" ) )
This formula returns the total of seconds with decimals represented in column A
= IFERROR( --TEXT( VALUE($A1), "[ss]" )
--RIGHT( SUBSTITUTE( $A1, ".", REPT( " ", 25 ) & ".", 1 ), 25 ),
--TEXT( --LEFT( SUBSTITUTE( $A1, ":", REPT( " ", 25 ), 1 ), 25 )
--TRIM( RIGHT( SUBSTITUTE( $A1, ":", REPT( " ", 25 ), 1 ), 25 ) ), "[ss]" )
--RIGHT( SUBSTITUTE( $A1, ".", REPT( " ", 25 ) & ".", 1 ), 25 ) )
You can use the following method to find diff in two cells of format YYYY-MM-DD hh:mm:ss.ms
( ex: if result is say 1.xx then xx is milliseconds )
=(a1-b1)*86400
note: before using this method ensure you convert the cell where the formula is written in to numbers
I've got following data as result from a query. As example 2 rows but in total around 30000 rows.
Some timestamps are empty because no row in that table.
agr_no
timestamp1
timestamp2
timestamp3
00000080064
2005-08-17-09.29.01.427337
2005-08-17-09.29.01.351888
00000080065
2002-04-29-15.04.58.714606
2013-11-18-13.11.46.494690
I would like to have in the next column an indication about which timestamp is the greatest.
For example:
agr_no
timestamp1
timestamp2
timestamp3
Result
00000080064
2005-08-17-09.29.01.427337
2005-08-17-09.29.01.351888
TS1
00000080065
2002-04-29-15.04.58.714606
2013-11-18-13.11.46.494690
TS3
Tried many things but always in trouble with the format of the timestamp so no comparison is possible.
Thanks a lot.
This is quite ugly but,
You can convert your values to dates and then just take the max date, or in this instance, the index of the matched date.
="TS" & MATCH(MAX(IFERROR(DATEVALUE(LEFT(A2:C2,10))+TIME(MID(A2:C2,12,2),MID(A2:C2,15,2),MID(A2:C2,18,2)+(RIGHT(A2:C2,6)/1000)),0)),IFERROR(DATEVALUE(LEFT(A2:C2,10))+TIME(MID(A2:C2,12,2),MID(A2:C2,15,2),MID(A2:C2,18,2)+(RIGHT(A2:C2,6)/1000)),0),0)
Note this assumes the formats of the string dates are either blank OR in strictly in the below format which is consistent with the limited inputs you gave us
yyyy-mm-dd.hh.mm.ss.######
FYI - Your milliseconds value is strange. On your first row the milliseconds actually equates to minutes
Assuming your data is located at [A1:D27] and based on this formula to convert the string TimeStamp to a double:
= IFERROR( VALUE( SUBSTITUTE( SUBSTITUTE( SUBSTITUTE( B2,
"-", " ", 3 ), ".", ":" ), ":", ".", 3 ) ), 0 )
Enter this FormulaArray in E2, then copy E2 downwards till last row:
= "TS" & MATCH( MAX(
IFERROR( VALUE( SUBSTITUTE( SUBSTITUTE( SUBSTITUTE( B2:D2,
"-", " ", 3 ), ".", ":" ), ":", ".", 3 ) ), 0 ) ),
IFERROR( VALUE( SUBSTITUTE( SUBSTITUTE( SUBSTITUTE( B2:D2,
"-", " ", 3 ), ".", ":" ), ":", ".", 3 ) ), 0 ), 0 )
FormulaArray are entered holding down ctrl+shift+enter simultaneously, the formula would be wrapped within { and } if entered correctly.
How can I use a formula in MS Excel to reverse a comma separated sting in row? The number of values are not always same so some rows have 3, 4 and some only one element.
So the output looks like following image
If you have Office 365 Excel then use This array formula:
=TEXTJOIN(",",,TRIM(MID(SUBSTITUTE(A2,",",REPT(" ",99)),((LEN(A2)-LEN(SUBSTITUTE(A2,",",""))+1)-ROW($XFD$1:INDEX(XFD:XFD,LEN(A2)-LEN(SUBSTITUTE(A2,",",""))+1)))*99+1,99)))
Being an array formula it needs to be confirmed with Ctrl-Shift-Enter instead of Enter when exiting edit mode.
If one does not have Office 365 Excel then vba will probably be the only choice.
Sloppy UDF solution:
Function REVERSESTRING(original As Range, delim As String)
Dim i As Long, reversed As String, arr As Variant
arr = Split(original.Value, delim)
For i = UBound(arr) To 0 Step -1
reversed = reversed & arr(i) & ","
Next i
REVERSESTRING = Left(reversed, Len(reversed) - 1)
End Function
A pure single-cell formula works in Excel 365; with that version there is no need for special array calculation entry, VBscripts, or helper columns:
Procedure
Make the following table in Excel:
Add column headings
Select headings, and press the Table icon on the Insert ribbon tab
Copy formula 1 & 2 below into the appropriate columns
Original | # | Reversed
---------+-----+-----------
| {1} | {2}
Place the string you would like to reverse in the first column.
Details and customization
'Original' column
(holds the delimited string value you wish to process)
Place your source data in this column (as seen in the example images)
'#' column
(counts number of items in delimited cell)
Note: (this column is completely optional; it simply shows the number of items in the original column)
{1} <- replace with formula #1:
=LET(
existingDelimiter, ","
, originalValue, [#[Original]]
, SUM(
LEN(originalValue)
- LEN(
SUBSTITUTE(
originalValue
, existingDelimiter
, ""
)
)
)
+1
)
(Note: If your list uses a delimiter other than a comma or if the first column of your table will have a different name, edit the above as appropriate using the same instructions given for formula #2 below.)
'Reversed' column
(reverses order of delimited items in list; optionally changes delimiter)
{2} <- replace with formula #2:
=LET(
existingDelimiter, ","
, newDelimiter, ","
, originalValue, [#[Original]]
, SUBSTITUTE(
ARRAYTOTEXT(
LET(
list,
IFERROR(
FILTERXML(
"<t><s>"
& SUBSTITUTE(
originalValue
,existingDelimiter
,"</s><s>"
)
& "</s></t>"
, "//s"
)
,""
)
,SORTBY(
list,
SEQUENCE(
ROWS(list)
,1
,ROWS(list)
,-1
)
)
)
,0
)
, ", "
, newDelimiter
)
)
Adjust the formula for what you are trying to accomplish by changing the values for existingDelimiter, newDelimiter, and originalValue, as necessary.
A. To reverse a comma-separated string, use the formula as written:
existingDelimiter, ","
, newDelimiter, ","
Example:
B. To reverse DNS names, replace the comma with a period in the definitions for existingDelimiter and newDelimiter:
existingDelimiter, "."
, newDelimiter, "."
This can be very useful for reverse DNS names (aka Java class names / Universal Type Indicators (UTIs) / etc.)
Example:
Replace "Original" in [#[Original]] with the name of your first column, if different.
A. If using just a single cell for input instead of a table column, replace [#[Original]] with the reference to that cell (e.g. B2):
, originalValue, B2
Example:
Explaination of the "Reversed" column formula:
By manually converting to XML, we can use the FilterXML function, which converts the data to an array.
Having the data in an array allows the use of the SortBy function.
SortBy reverses the array by using a helper array created with the Sequence function.
Finally, the ArrayToText functions converts this (now reverse-ordered) array back to a text string that will fit in a single spreadsheet cell.
This is what allows us not to need a loop, helper columns, or VBScript.
Bonus column
To extract a specific term from a list, use the following formula in another table column:
(Change the number in termNumber to the desired value):
=LET(
existingDelimiter, ","
, originalValue, [#[Original]]
, termNumber, "[2]"
, IFERROR(
FILTERXML(
"<t><s>"
& SUBSTITUTE(
originalValue
, existingDelimiter
, "</s><s>"
)
& "</s></t>"
, "//s"
& termNumber
)
, ""
)
)
Example:
Other notes
Needed:
Excel 365 (for at least FilterXML and Let functions, and dynamic arrays) (*)
It might work with other versions but I have not tested those. Please note in the comments if you notice other or future versions (e.g. Excel 2022) work with this.
Not needed:
dynamic array entry
VBscript
macro-enabled files
(*) This can be done without the Let function, but using Let allows the calculation to be edited / repurposed with less chance for user error.
Note: When the Lambda function is released (hopefully later in 2021) then this all can be wrapped up in a named worksheet function
Bonus: to edit more complex Excel formulas with code highlighting (and other features such as auto-intents and folding), try a text editor with Swift programming language support. Examples:
VSCode editor (free; cross-platform)
Until there is an Excel-specific extension available, install a Swift VSCode extension such as this: Swift language VSCode extension (it seems to work quite well to provide code highlighting and folding)
Notepad++ (free; Windows-only)
Select "Swift" from the "Language" menu
Inspired by (apologies if I've forgotten someone):
https://www.howtoexcel.org/tutorials/split-text-by-delimiter/
https://www.sumproduct.com/news/article/have-you-got-what-it-text-introducing-arraytotext-and-valuetotext
https://exceljet.net/formula/reverse-a-list-or-range
I'm currently using a DAX formula to try and remove numbers from a string. The string is the first half of a postcode. I've therefore typed the following formulae
=if(Istext(mid([Postcode District],1,1)),(left([postcode district],2)),(left([postcode district],1)))
what this SHOULD do is see if the second character is text and return 2 letters if it is, one if it isn't.
For example, input CA1 should return CA whilst B22 should return B
This simply isn't doing that, and I'm not sure why. all that is being returned is the first two letters whether the second letter is text OR numeric.
The MID function takes a string as an argument and will always return a string (even if it looks like a number), so your IsText is always TRUE.
Try this
=if(IsNumber(mid([Postcode District],1,1)*1),(left([postcode district],1)),(left([postcode district],2)))
ended up using =SUBSTITUTE(substitute(SUBSTITUTE( SUBSTITUTE( SUBSTITUTE( SUBSTITUTE( SUBSTITUTE( SUBSTITUTE( SUBSTITUTE( SUBSTITUTE([Postcode District],"1"," "),"2"," "), "3"," "),"4"," "),"5"," "), "6"," "),"7"," "),"8"," "),"9"," "),0,"")
(as its DAX it'll accept this - it's too many nested functions for standard XL)
As it's quite an ugly formulae, I'll be trying other people's answers too, to see if there's a more elegant solution :)