Skip to content

Instantly share code, notes, and snippets.

@halbuki
Last active March 10, 2026 20:51
Show Gist options
  • Select an option

  • Save halbuki/4dd8b87983c963002f9e2e0217c06567 to your computer and use it in GitHub Desktop.

Select an option

Save halbuki/4dd8b87983c963002f9e2e0217c06567 to your computer and use it in GitHub Desktop.
Excel Lambda functions for Power Query Text functions
/* USE NAMESPACE "Text" */
AfterDelimeter = LAMBDA(_text, delimiter, [_index],
LET(
Help, TRIM(TEXTSPLIT(
"DESCRIPTION:->Returns the part of _text after the given delimiter. If _index is omitted, returns after the first delimiter\nVERSION:->1.0\nPARAMETERS:->_text as text, delimiter as text, _index as number (optional)",
"->",
"\n"
)),
ErrorMessages, {"_text is not text."; "delimiter is not text."; "_index is not a number."},
_err1, IF(NOT(ISTEXT(_text)), #VALUE!, FALSE()),
_err2, IF(NOT(ISTEXT(delimiter)), #VALUE!, FALSE()),
_err3, IF(AND(NOT(ISOMITTED(_index)), NOT(ISNUMBER(_index))), #VALUE!, FALSE()),
ErrorArray, VSTACK(ISERROR(_err1), ISERROR(_err2), ISERROR(_err3)),
Messages, FILTER(ErrorMessages, ErrorArray, ""),
Result, _text,
Error, IF(OR(ErrorArray), 3, IF(OR(ISERROR(Result)), 2, 1)),
CHOOSE(Error, Result, Help, Messages)
)
);
At = LAMBDA(_text, _index,
LET(
Help, TRIM(TEXTSPLIT(
"DESCRIPTION:->Returns the character at the specified index of the text\nVERSION:->1.0\nPARAMETERS:->_text as text, _index as number",
"->",
"\n"
)),
ErrorMessages, {"_text is not text."; "_index is not a number."},
_err1, IF(NOT(ISTEXT(_text)), #VALUE!, FALSE()),
_err2, IF(NOT(ISNUMBER(_index)), #VALUE!, FALSE()),
ErrorArray, VSTACK(ISERROR(_err1), ISERROR(_err2)),
Messages, FILTER(ErrorMessages, ErrorArray, ""),
Result, MID(_text, _index, 1),
Error, IF(OR(ErrorArray), 3, IF(OR(ISERROR(Result)), 2, 1)),
CHOOSE(Error, Result, Help, Messages)
)
);
BeforeDelimeter = LAMBDA(_text, delimiter, [_index],
LET(
Help, TRIM(TEXTSPLIT(
"DESCRIPTION:->Returns the part of _text before the given delimiter. If _index is omitted, returns before the first delimiter\nVERSION:->1.0\nPARAMETERS:->_text as text, delimiter as text, _index as number (optional)",
"->",
"\n"
)),
ErrorMessages, {"_text is not text."; "delimiter is not text."; "_index is not a number."},
_err1, IF(NOT(ISTEXT(_text)), #VALUE!, FALSE()),
_err2, IF(NOT(ISTEXT(delimiter)), #VALUE!, FALSE()),
_err3, IF(AND(NOT(ISOMITTED(_index)), NOT(ISNUMBER(_index))), #VALUE!, FALSE()),
ErrorArray, VSTACK(ISERROR(_err1), ISERROR(_err2), ISERROR(_err3)),
Messages, FILTER(ErrorMessages, ErrorArray, ""),
Result, _text,
Error, IF(OR(ErrorArray), 3, IF(OR(ISERROR(Result)), 2, 1)),
CHOOSE(Error, Result, Help, Messages)
)
);
BetweenDelimeters = LAMBDA(_text, _startDelimiter, _endDelimiter, [_startIndex], [_endIndex],
LET(
Help, TRIM(TEXTSPLIT(
"DESCRIPTION:->Returns the text between start and end delimiters. Optional indices can specify which occurrence of the delimiters to use\nVERSION:->1.0\nPARAMETERS:->_text as text, _startDelimiter as text, _endDelimiter as text, _startIndex as number (optional), _endIndex as number (optional)",
"->",
"\n"
)),
ErrorMessages, {"_text is not text."; "_startDelimiter is not text."; "_endDelimiter is not text."; "_startIndex is not a number."; "_endIndex is not a number."},
_err1, IF(NOT(ISTEXT(_text)), #VALUE!, FALSE()),
_err2, IF(NOT(ISTEXT(_startDelimiter)), #VALUE!, FALSE()),
_err3, IF(NOT(ISTEXT(_endDelimiter)), #VALUE!, FALSE()),
_err4, IF(AND(NOT(ISOMITTED(_startIndex)), NOT(ISNUMBER(_startIndex))), #VALUE!, FALSE()),
_err5, IF(AND(NOT(ISOMITTED(_endIndex)), NOT(ISNUMBER(_endIndex))), #VALUE!, FALSE()),
ErrorArray, VSTACK(ISERROR(_err1), ISERROR(_err2), ISERROR(_err3), ISERROR(_err4), ISERROR(_err5)),
Messages, FILTER(ErrorMessages, ErrorArray, ""),
Result, _text,
Error, IF(OR(ErrorArray), 3, IF(OR(ISERROR(Result)), 2, 1)),
CHOOSE(Error, Result, Help, Messages)
)
);
Clean = LAMBDA(_text,
LET(
Help, TRIM(TEXTSPLIT(
"DESCRIPTION:->Removes non-printable characters from text\nVERSION:->1.0\nPARAMETERS:->_text as text",
"->",
"\n"
)),
ErrorMessages, {"_text is not text."},
_err1, IF(NOT(ISTEXT(_text)), #VALUE!, FALSE()),
ErrorArray, VSTACK(ISERROR(_err1)),
Messages, FILTER(ErrorMessages, ErrorArray, ""),
Result, CLEAN(_text),
Error, IF(OR(ErrorArray), 3, IF(OR(ISERROR(Result)), 2, 1)),
CHOOSE(Error, Result, Help, Messages)
)
);
Contains = LAMBDA(_text, _substring,
LET(
Help, TRIM(TEXTSPLIT(
"DESCRIPTION:->Returns TRUE if _substring is found in _text, otherwise FALSE\nVERSION:->1.0\nPARAMETERS:->_text as text, _substring as text",
"->",
"\n"
)),
ErrorMessages, {"_text is not text."; "_substring is not text."},
_err1, IF(NOT(ISTEXT(_text)), #VALUE!, FALSE()),
_err2, IF(NOT(ISTEXT(_substring)), #VALUE!, FALSE()),
ErrorArray, VSTACK(ISERROR(_err1), ISERROR(_err2)),
Messages, FILTER(ErrorMessages, ErrorArray, ""),
Result, IFERROR(FIND(_substring, _text), FALSE),
Error, IF(OR(ErrorArray), 3, IF(OR(ISERROR(Result)), 2, 1)),
CHOOSE(Error, Result, Help, Messages)
)
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment