Probably not the most efficient solution... but here's a way without VBA: (line break added for readability)
= "AP#"&MID(MID(A1,FIND("AP#",A1)+3,999),1,
MAX((ISNUMBER(MID(MID(A1,FIND("AP#",A1)+3,999),{1,2,3},1)+0)+0)*{1,2,3}))
EDIT
Slightly better solution:
= MID(A1,FIND("AP#",A1),
MAX(ISNUMBER(MID(MID(A1,FIND("AP#",A1)+3,999),{1,2,3},1)+0)*{1,2,3})+3)
EDIT (again)
As pointed out in comment, this does not take into account something like AP#1-1
. Here is the updated formula that will take this into account:
= MID(A1,FIND("AP#",A1),IFERROR(MATCH(FALSE,
ISNUMBER(MID(MID(A1,FIND("AP#",A1)+3,3),{1,2,3},1)+0),0),4)+2)
As requested, here is how this formula works. I'll break it down step by step. This is a pretty long explanation but if you just take it one step at a time, I think you should be able to understand the entire formula. I'm going to explain what is going on from the inside out.
FIND("AP#",A1)
returns the character index number in A1
where the first instance of AP#
appears in A1
.
For simplicity, I will refer to FIND("AP#",A1)
as <x1>
in the next step.
MID(A1,<x1>+3,3)
returns the 3 characters in A1
that appear immediately after AP#
. It only returns 3 characters because from the original problem, you said that up to 3 numbers can appear after AP#
.
(Quick note: Originally I had this part of the formula as MID(A1,<x1>+3,999)
but after making this explanation, I realized that 999
could be reduced to 3
. 999
would still work, just that 3
is simpler and makes the formula more efficient.)
I will refer to this value MID(A1,<x1>+3,3)
as <x2>
in the next step.
MID(<x2>,{1,2,3},1)
essentially converts <x2>
which is a string of 3 characters, to a array of 3 strings, each string 1 character long. In other words, if <x2>
is (for example), "1-2"
, then that means MID(<x2>,{1,2,3},1)
is {"1","-","2"}
. It is necessary to convert a string of 3 characters to a 1x3 array of single characters in order to individually analyze each character.
I will refer to MID(<x2>,{1,2,3},1)
as <x3>
in the next step.
<x3>+0
may seem like a simple step but there is a lot going on here. Keep in mind <x3>
is still an array of strings, not numbers (even if they look like numbers). The +0
will convert all strings that look like numbers to numbers, and will convert all strings that don't look like numbers to an error value. (In this case, #VALUE!
.)
Sticking with our same example, {"1","-","2"}+0
will equal {1,#VALUE!,2}
.
I will refer to <x3>+0
as <x4>
in the next step.
MATCH(FALSE,ISNUMBER(<x4>),0)
returns the first index of <x4>
where it is not a number. The idea here is to find the index of the first non-number, and then include everything up to that index (minus one).
Sticking with our same example, MATCH(FALSE,ISNUMBER({1,#VALUE!,2}),0)
would return 2
, because the 2nd index in {1,#VALUE!,2}
is the first index that is not a number.
I will refer to MATCH(FALSE,ISNUMBER(<x4>),0)
as <x5>
in the next step.
It is possible that all values in <x4>
are numbers, in which case <x5>
would return an error because it can't find a match for a non-number. IFERROR(<x5>,4)
fixes this issue. It returns the value 4
if all values in <x5>
are numbers. The reason to return 4
is because we are basically saying that all 3 of the characters following AP#
are numbers, so the first index that we aren't considering after AP#
is the 4th index.
I will refer to IFERROR(<x5>,4)
as <x6>
in the next step.
<x6>+2
may seem like a strange calculation, and it is, so I will write it a different way that will make more sense: (<x6>-1)+3
Remember what <x6>
represents here: It is the index of the first non-number that appears in the string of 3 characters after AP#
. Therefore, <x6>-1
is the number of characters to include after AP#
.
Now, why add 3? (<x6>-1)+3
is necessary to include the 3 characters in AP#
itself. This will make sense in the next step.
I will refer to <x6>+2
as <x7>
in the next step.
MID(A1,FIND(AP#,A1),<x7>)
returns a portion of string A1
, starting at the A
in AP#
and spanning <x7>
characters. And how large is <x7>
? It is however many numbers are in the AP#
code, plus 3. (Again, we must add 3 to include the 3 AP#
characters themselves in the calculation.)
This is the entire calculation.
Come to think of it, you may want to wrap an IFERROR
around the entire calculation to take care of cases where AP#
isn't found in the string, e.g. something like:
= IFERROR(MID(A1,FIND("AP#",A1),IFERROR(MATCH(FALSE,
ISNUMBER(MID(MID(A1,FIND("AP#",A1)+3,3),{1,2,3},1)+0),0),4)+2),"no match")
But really that is your call. I'm not sure if this is necessary.