BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
Trishjais
Obsidian | Level 7

Hello Expert,

 

I need to swap the characters in a variable , how can I achieve this.

Sample i/p and o/p is as following:

 

InputOutput
MichaelLhameci
Jack@gmail.comkcja@gmail.com
AO987JJ9J78JOA
96502345676953035947
1 ACCEPTED SOLUTION

Accepted Solutions
s_lassen
Meteorite | Level 14

I suppose what you want is to have the letters randomly permuted. I think the easiest is to use PROC FCMP to write a function for that:

proc fcmp outlib=work.funcs.permute;
  function permute(name$,type$) $200;
  length name2 $200 i j 8;
  do i=1 to length(name);
    j=rand('integer',length(name));
    substr(name2,i,1)=substr(name,j,1);
    substr(name,j)=substr(name,j+1);
    end;
  select(upcase(substr(type,1,1)));
    when('N') do; /* "name" or "nice", first letter capital */
      name2=lowcase(name2);
      substr(name2,1,1)=upcase(substr(name2,1,1));
      end;
    when(' ','I'); /* keep as is/irrelevant */
    when('U') name2=upcase(name2);
    when('L') name2=lowcase(name2);
    otherwise put 'WARNING: wrong type parameter' type=;
    end;
  return(name2);
  endsub;
run;
options cmplib=work.funcs; /* you may want to use a permanent library instead of WORK */

The function takes a random letter from the input, moves it to the output, discards it from the input, and repeats until input is empty. After that, the TYPE option is processed, see the code.

 

With this data

data have;
  length name surname $8 email $25 code $8;
  input name surname email code;
cards;
Jack Vance Jack@gmail.com A1245
Peter Sellers Peter@Pink.Panter.com B44444
;run;

the function can be used like this:

data want;
  if _N_=1 then
    call streaminit(3);
  set have;
  name=permute(name,'N');
  surname=permute(surname,'N');
  mail_pref=permute(scan(Email,1,'@'),'N');
  substr(email,1,length(mail_pref))=mail_pref;
  drop mail_pref;
  code=permute(code,' ');
run;

The STREAMINIT call makes the results repeatable (same result every time) by initiating the stream for the RAND calls. If you drop it, you will get different results every time you run the code.

View solution in original post

5 REPLIES 5
ChrisNZ
Tourmaline | Level 20

I see no obvious pattern. 

Can you explain how the characters are moved?

In the last example the 2 has simply disappeared.

Please put lot more effort explaining your needs.

 

s_lassen
Meteorite | Level 14

I suppose what you want is to have the letters randomly permuted. I think the easiest is to use PROC FCMP to write a function for that:

proc fcmp outlib=work.funcs.permute;
  function permute(name$,type$) $200;
  length name2 $200 i j 8;
  do i=1 to length(name);
    j=rand('integer',length(name));
    substr(name2,i,1)=substr(name,j,1);
    substr(name,j)=substr(name,j+1);
    end;
  select(upcase(substr(type,1,1)));
    when('N') do; /* "name" or "nice", first letter capital */
      name2=lowcase(name2);
      substr(name2,1,1)=upcase(substr(name2,1,1));
      end;
    when(' ','I'); /* keep as is/irrelevant */
    when('U') name2=upcase(name2);
    when('L') name2=lowcase(name2);
    otherwise put 'WARNING: wrong type parameter' type=;
    end;
  return(name2);
  endsub;
run;
options cmplib=work.funcs; /* you may want to use a permanent library instead of WORK */

The function takes a random letter from the input, moves it to the output, discards it from the input, and repeats until input is empty. After that, the TYPE option is processed, see the code.

 

With this data

data have;
  length name surname $8 email $25 code $8;
  input name surname email code;
cards;
Jack Vance Jack@gmail.com A1245
Peter Sellers Peter@Pink.Panter.com B44444
;run;

the function can be used like this:

data want;
  if _N_=1 then
    call streaminit(3);
  set have;
  name=permute(name,'N');
  surname=permute(surname,'N');
  mail_pref=permute(scan(Email,1,'@'),'N');
  substr(email,1,length(mail_pref))=mail_pref;
  drop mail_pref;
  code=permute(code,' ');
run;

The STREAMINIT call makes the results repeatable (same result every time) by initiating the stream for the RAND calls. If you drop it, you will get different results every time you run the code.

VENKATAMAHESH
Calcite | Level 5

@Trishjais 

data input;
input lines$20.;
lines_=lines;

/*temporary replacements to avoid errors at macro processing*/
lines_=translate(lines, '2', '.');
lines_=translate(lines_, '1', '@');
lines;
Michael
Jack@gmail.com
AO987JJ
9650234567
;
run;

 

/*dynamically create macro variables from the above data*/
data _null_;
set input;
call symputx("val"||left(_N_), lowcase(lines_));
run;

 

/*empty data set to use at step 4 of macro def*/
data final;
length str $20;
str="";
run;

 

/*use parmbuff to support random parameters at macro call*/
%macro result/parmbuff;
/*create length macro var based on the parameters passed to this macro def.
adding '1' is required since shuffled indices are followed by data set name*/
%let length = %eval(1+%sysfunc(countc(&syspbuff, ',')));
%let str=;

 

/*loop to replace chars*/
%do i=2 %to &length;
/*extracting specified(random) chars from the original string*/
%let str = &str.%substr(%scan(&syspbuff, 1), %scan(&syspbuff, &i), 1);
%end;

 

/*storing each shuffled value in a separate data set*/
data ds_%scan(&syspbuff, 1);
/*optional length statement to avoid concatenation warnings*/
length str $20;
str="&str";
run;

 

/*to join with other calls - step 4*/
data final(where=(not missing(str)));
set final ds_%scan(&syspbuff, 1);
run;
/*macro name after %mend is optional*/
%mend result;

 

/*pass macro vars with desired shuffle order*/
/*semicolon at the end of macro call is optional*/
%result(&val1, 7, 1, 2, 3, 4, 5, 6)
%result(&val2, 4, 3, 1, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)
%result(&val3, 3, 6, 5, 4, 7, 2, 1)
%result(&val4, 2, 1, 3, 6, 4, 6, 3, 1, 7, 10)


/*improvements*/
data final;
set final;
/*back to normal*/
str=translate(str, '@', '1');
str=translate(str, '.', '2');
run;

 

data output(drop=lines_ rename=(lines=input str=output));
/*make it side by side and some cosmetic changes*/
merge input final;
if findc(str, '@') then str=lowcase(str);
else if anydigit(str) then str=upcase(str);
else str=propcase(str);
run;

 

/*deleting extra data sets from work lib to save ram*/
proc datasets lib=work nolist;
save output;
quit;

VENKATAMAHESH
Calcite | Level 5

Hi,

You may check the attached solution for that problem.

BPD
Obsidian | Level 7 BPD
Obsidian | Level 7

Hi,

 

The REVERSE function sounds like all you need. I'm assuming there were a few typo's in your question and that you wanted a literal reverse of the character string presented.

eg.

Data _null_;

 cs = 'CharacterString';

Put cs=;

 rcs = reverse (cs);

put rcs=;

run;

 

This will show in the log as:

cs  = CharacterString

rcs = gnirtSretcarahC

 

Note, if the value of the character string (cs) was 'Char    ' then the reversed string would be '    rahC' so you may want to use one of SAS's many blank removing functions to strip away the leading blanks - a simple use of the LEFT function here would remove the leading blanks.  e.g. LEFT(REVERSE(cs)) would give you 'rahC    '.

 

If this isn't what you require perhaps you can specify more detailed requirements?

 

BPD

sas-innovate-2024.png

Available on demand!

Missed SAS Innovate Las Vegas? Watch all the action for free! View the keynotes, general sessions and 22 breakouts on demand.

 

Register now!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 5 replies
  • 956 views
  • 0 likes
  • 5 in conversation