SAS: Removing first and last characters and numbers of a string - sas

I am searching for hours but nothing seems to work so far. I tried reverse, substr and scan but it's all not doing what I need. I am so thankful for any answer.
I have a string in a following way (lenghts vary in the dataset):
1CDF534R6
Now, I need 2 new variables:
a) 534, i.e. the middle numbers
Something like: Give me all numbers and then cut the first and last (that would work in my case).
b) 1CDF536
Just removing the last two characters
Especially the first one is important and would be great if it works somehow.
Best

In the first case use a compress function. Keep only digits.
data result;
source = "1CDF534R6";
a = compress(source, , 'kd');
a = substr(a, 2, lengthn(a) - 2);
b = substr(source, 1, lengthn(source) - 2);
run;

Related

Universe OCONV argument for zero-padding

I'm looking for some argument (ARG) such that this code:
A = 5
B = OCONV(A,'ARG5')
PRINT B
will print to the screen
00005
Anybody know something which will do this for me?
In Universe I would use the MR% conversion code. Just be aware that it will truncate anything longer than 5 characters.
A = 5
B = OCONV(A,'MR%5')
PRINT B
I use this a lot when I need to use EVAL in a conditional or as an aggregate function in a SQL or other TCL statement like to find the record with the most fields in a file.
SELECT MAX(EVAL "DCOUNT(#RECORD,#FM)") FROM VOC;
SELECT MAX(EVAL "OCONV(DCOUNT(#RECORD,#FM),'MR%8')") FROM VOC;
Masking aside these generally return 2 different values on our system.
I am using UniData, but looking at the commands reference manual I can't see anything quite right, in terms of one simple argument to OCONV, or similar. I came up with these (somewhat kludgy) alternatives, though:
NUMLEN=5
VALUE=5
PRINT CHANGE(SPACES(NUMLEN-LEN(VALUE))," ","0"):VALUE
Here you are using the SPACES function to create that amount of space characters and then convert them to zeros.
PRINT OCONV(VALUE,"MR":NUMLEN:"(#####)")
This is using OCONV but has to define a string with the "mask" to only shew the final 5 digits. So if NUMLEN changes then the mask string definition would have to change.
PRINT OCONV(VALUE,"MR":NUMLEN)[3,NUMLEN]
This version uses OCONV but prints starting at the 3rd character and shews the next NUMLEN characters, therefore trimming off the initial "0." that is made by using the "MR" parameter
PADDED.VALUE = VALUE 'R%5' is the simplest way to do this.

Find group of strings starting and ending by a character using regular expression

I have a string, and I want to extract, using regular expressions, groups of characters that are between the character : and the other character /.
typically, here is a string example I'm getting:
'abcd:45.72643,4.91203/Rou:hereanotherdata/defgh'
and so, I want to retrieved, 45.72643,4.91203 and also hereanotherdata
As they are both between characters : and /.
I tried with this syntax in a easier string where there is only 1 time the pattern,
[tt]=regexp(str,':(\w.*)/','match')
tt = ':45.72643,4.91203/'
but it works only if the pattern happens once. If I use it in string containing multiples times the pattern, I get all the string between the first : and the last /.
How can I mention that the pattern will occur multiple time, and how can I retrieve it?
Use lookaround and a lazy quantifier:
regexp(str, '(?<=:).+?(?=/)', 'match')
Example (Matlab R2016b):
>> str = 'abcd:45.72643,4.91203/Rou:hereanotherdata/defgh';
>> result = regexp(str, '(?<=:).+?(?=/)', 'match')
result =
1×2 cell array
'45.72643,4.91203' 'hereanotherdata'
In most languages this is hard to do with a single regexp. Ultimately you'll only ever get back the one string, and you want to get back multiple strings.
I've never used Matlab, so it may be possible in that language, but based on other languages, this is how I'd approach it...
I can't give you the exact code, but a search indicates that in Matlab there is a function called strsplit, example...
C = strsplit(data,':')
That should will break your original string up into an array of strings, using the ":" as the break point. You can then ignore the first array index (as it contains text before a ":"), loop the rest of the array and regexp to extract everything that comes before a "/".
So for instance...
'abcd:45.72643,4.91203/Rou:hereanotherdata/defgh'
Breaks down into an array with parts...
1 - 'abcd'
2 - '45.72643,4.91203/Rou'
3 - 'hereanotherdata/defgh'
Then Ignore 1, and extract everything before the "/" in 2 and 3.
As John Mawer and Adriaan mentioned, strsplit is a good place to start with. You can use it for both ':' and '/', but then you will not be able to determine where each of them started. If you do it with strsplit twice, you can know where the ':' starts :
A='abcd:45.72643,4.91203/Rou:hereanotherdata/defgh';
B=cellfun(#(x) strsplit(x,'/'),strsplit(A,':'),'uniformoutput',0);
Now B has cells that start with ':', and has two cells in each cell that contain '/' also. You can extract it with checking where B has more than one cell, and take the first of each of them:
C=cellfun(#(x) x{1},B(cellfun('length',B)>1),'uniformoutput',0)
C =
1×2 cell array
'45.72643,4.91203' 'hereanotherdata'
Starting in 16b you can use extractBetween:
>> str = 'abcd:45.72643,4.91203/Rou:hereanotherdata/defgh';
>> result = extractBetween(str,':','/')
result =
2×1 cell array
{'45.72643,4.91203'}
{'hereanotherdata' }
If all your text elements have the same number of delimiters this can be vectorized too.

PythonQuestion on Longest Common Substring(LCS) algorithm

I'm pretty new to Python, it's my first programming language, and I've wanted to work on some manual data structure manipulation and playing around.
I've recently been learning the basic algorithm for solving the LCS problem, and I understand how it works besides one line of code that I for some weird reason can't seem to convince myself I am grasping entirely.
this is the code I've been using to learn from after I couldn't get it down myself quite right.
EDIT 2: Anyway to make this work with an input of two lists of integers?**I figured out that I was understanding my original question correctly, but would anyone know how I could make this work with a **list of integers? I tried converting S and T to a string of comma separated values, which worked in matching some of the characters, but even then it rarely worked in most test-cases. I'm not sure why it wouldn't, as it is still just two strings being compared, but with commas.
def lcs(S,T):
m = len(S)
n = len(T)
counter = [[0]*(n+1) for x in range(m+1)]
longest = 0
lcs_set = set()
for i in range(m):
for j in range(n):
if S[i] == T[j]:
c = counter[i][j] + 1
counter[i+1][j+1] = c
if c > longest:
lcs_set = set()
longest = c
lcs_set.add(S[i-c+1:i+1])
elif c == longest:
lcs_set.add(S[i-c+1:i+1])
return lcs_set
Now my issue is understanding is the line : lcs_set.add(S[i-c+1:i-1])
I understand that the counter is incremented when a match is found, to give longest the length of the substring. So, to make it easy, if S = Crow and T = Crown, when you reach w, the last match, the counter is incremented to 4, and i is at index 3 of S.
Does this mean I am to read this as: i (index3 on S, the W) - c (4), so 3-4 = -1, so 3-4+1 = 0 (at C) and for the right side of the slice: i(3) + 1 = 4(N, but will not be included, obviously), meaning we end with S[0:4], Crow, to LCS_Set?
If that is the case, I guess I am confused as to why we are adding the whole substring to the set, and not just the newest matched character?
If I understand right, it is updating LCS_set with the entire slice of the current matched substring, so if it were on the second match, R, the counter would be at 2, i would be at 1, and it would be saying S[1-2+1:i(1)+1], so 1-2 = -1, -1 + 1 = 0(C) up to i(1)+1 = 2 (leaving us with S[0:2], or CR), so each time around, the set is updated with the entire substring, and not just the current index.
It's not really a problem, I just want to make sure I'm understanding this correctly.
I would really appreciate any input, or any tips anyone might see with my current logic!!
EDIT:
I just realized I was totally forgetting that the position at C is the current counter number, therefore it obviously wouldn't be updating the LCS_set with the current max match number, and it can't update it with just the current matched letter, so it has to take the slice of the substring in order to update the LCS_Set.
Thanks in advance!

Regex Split: Split column into Name, percentage andsolvent

Looking for a regex that can split expressions like:
A-6-b 10/%XYZ
into:
A-6-b
10%
/XYZ
Note that the first group can also contain spaces and numbers:
AQDF 100 56%/ABC
and percentage can be a float:
SFSDF 0.1%/ABC
I've come up with (^[A-Z\s\d-]*)(?!%)(\d+%)(.*$) but this doe snot match any percentages that are floats and more importantly even simple examples like ABC 10%/XYZ fail because the first digit of the percentage is assigned to the first capturing group.
Any idea how I can achieve what I want? I'm not a regex expert...
EDIT: fixed errors in example
EDIT2:
The examples are not complete. Here one more:
ABC Dwsd 0.01%/XYZ QST
First part can contain spaces
Last Part can contain spaces
number can be a float
Super simple:
/^(.*) ([1-9][0-9]*(?:\.[0-9]+)?%)(.*)$/
The most easily identifiable item is your percentage, so the ([1-9][0-9]*(?:\.[0-9]+)?%) part deals with finding that.
Then it's simply a case of getting everything before (excluding the final space) to get the name, and everything after to get the solvent.
Done.
Don't overcomplicate this by using one unreadable regex.
Based on what you've said, your separators are well defined (the last space and the last %). In JavaScript, for example, you could use:
var str = "A-6-b 10/%XYZ";
var firstSeparator = str.lastIndexOf(' ');
var secondSeparator = str.lastIndexOf('%');
var name = str.substring(0, firstSeparator);
var percentage = str.substring(firstSeparator + 1, secondSeparator + 1); // we want to include the % separator in this one
var solvent = str.substring(secondSeparator + 1);
console.log(name, percentage, solvent);
Working JSFiddle: http://jsfiddle.net/rL5uymhm/
(There may be a typo in your question, as your examples differ on where the / symbol appears. So the code may need tweaking. My point still stands – don't use a regex for the sake of it when there is a more readable alternative.)
IF you really want to use a regex, /^(.+ )([^%]+%)(.*)$/ should work.
I try this Let me know if you have any problem in comment.
((?:(?!\s*[0-9]*\/%).)*)\s*([\d\/%]*)\s*(.*)
SEE DEMO : http://regex101.com/r/lL8oN4/1
This one works for me (using PCRE):
/^(.+) ([0-9.]+)[\/%]+([^\/]+)$/

How to find number of words in a phrase with spaces removed by checking v. dictionary

I have a word, for example "ilikesamsung", and a dictionary of words, for example:
{"i","like","the","king","sam","sung","samsung"}
I want to calculate number of spaces in this word, if we break it into the dictionary words. In the above example, the string is broken into "i like samsung", and there are 2 spaces.
How can it be done?
Elaboration:
Problem has 2 parts-
Output Yes if this word can be broken.
Calculate number of spaces
What I have tried:
I have solved First part by Dynamic Programming.,
For the second part:
I have extended DP method to solve part2 . I took an array and I stored index at which word ends. arr[]={1,0,0,0,1,0,0,1,0,0,0,1}
this gives answer 3.
I want answer 2, for this array should be {1,0,0,0,1,0,0,0,0,0,0,1}.
Any advises?
You can use Dynamic Programming for this task:
f(0) = 0
f(i) = MIN { f(i-j) + (Dictionary.contais(s.substring(i-j,i)?1:INFINITY } for each j=1,...,i
The above finds the number of words in your string, so the final answer is f(#characters)-1
The idea is to do an "exhaustive search" - for a given new character - try to connect it to a word, and recursively invoke on what's left from the string.
This can be done pretty efficiently with DP techniques.