DATA Step, Macro, Functions and more

Array using variable for subscript

Accepted Solution Solved
Reply
Contributor
Posts: 22
Accepted Solution

Array using variable for subscript

I am running into a INVALID NUMERIC error when feeding an array subscript with a variable value. 
I succesfully define the array:
 
Array BRAND (2) VW AUDI (1 5);
Put BRAND(VW);
 
/* as expected I get back the numeric result 1 */
 
But if I use the same string generated by the variable in the data set, here MAKE, it runs into an error. 
 

This happens even when I apply STRIP to the character variable MAKE.

 
Put BRAND(MAKE);
 
I circumvent this by proceeding an if-then logic to convert MAKE into values 1 or 2.
Then the array accepts the variable value, this time numeric, for the subscript.
 
 But why SAS derails with the other alternative?
 
Thanks, Arne
 

Accepted Solutions
Solution
‎02-03-2017 07:43 PM
Super User
Super User
Posts: 7,039

Re: Array using variable for subscript

You reference members of an array with an integer index into the array. Not with a NAME.  If you want to reference a variable by name then just reference the variable, there is no need to use an array.  

 

If you need to convert a character variable with the name of a variable into the value of the variable the most direct way is to use the VVALUEX() function, but that would return the value as a character string.  If you wanted the value converted back to a number then use INPUT() function.

length vw audi 8 make $32 ;
array brand vw audi (50.5 25.4);
model = 'VW';
mpg = input(vvaluex(model),32.);

Or you could find a method to convert the name to the index to use with the array.  For example using the WHICHC() function might work.

 

model = 'VW';
mpg = brand(whichc(model,'VW','AUDI'));

Try this example:

data _null_;
  length vw audi 8 make $32 ;
  array brand vw audi (50.5 25.4);
  input make ;
  mpg1 = input(vvaluex(make),32.);
  mpg2 = brand(whichc(make,'VW','AUDI'));
  put (_all_) (=);
cards;
VW
AUDI
;;;;

View solution in original post


All Replies
Super User
Posts: 11,343

Re: Array using variable for subscript

Indices for arrays must be numeric. Look at the results for

Put BRAND("VW");

 

You get something similar to:

NOTE: Invalid numeric data, 'VW' , at line 295 column 11.

ERROR: Array subscript out of range at line 295 column 5.

 

Your log should have had that error.

 

BTW it really helps to post code and log results in a code box using the {i} icon. copying you code introduced a bunch of . when pasted into a code editor.

Contributor
Posts: 22

Re: Array using variable for subscript

Thank you. 

I had exactly the log you describe.

 

But why does it work when I use this code for the subcript:

PUT BRAND(VW);

This works!

 

Sorry for leaving out the copied code, but I have some compliance issues Smiley Happy

I write from my personal laptop and SAS is installed on my business laptop and without making crazy things from home I cannot easily copy-paste the code. 

Trusted Advisor
Posts: 1,554

Re: Array using variable for subscript

Check what type are the variables:

VW is probably numeric and its value is either 1 or 2.

MAKE is probably defined as character with values '1' or '2';

When you use strip(var) the output is a character type.

Contributor
Posts: 22

Re: Array using variable for subscript

Thanks for your comment. I have tried without success defining the array as $
Array BRAND (2) VW AUDI ("1" "2");

Just to make clear. The variable MAKE of the data step where I declare the array is of type character and carries the values "VW" and "AUDI".

Put BRAND (1);
--> 1 OK
Put BRAND (VW);
--> 1 OK

Put STRIP(MAKE);
--> "VW" OK
Put BRAND(MAKE);
INVALID NUMERIC NOK
Super User
Posts: 19,772

Re: Array using variable for subscript

That tells us nothing. Why are you trying to do this? 

 

VW is a variable that's created with the array reference that ends up being circular. You've assigned it a value of 1, then it pulls the first value out of the index. Like I mentioned earlier, this works purely by chance. Change the 1/2 in your array to 5/10 and it won't work anymore.

 

What problem are you trying to solve with this code.

Super User
Posts: 19,772

Re: Array using variable for subscript

It might work, but that's really just by chance for your particular example. It's not the correct method to work. Typically you can't access the variable value via an array. 

 

If you want to access the value of a variable look at the VVALUEX function which can work off the variable name, not an index.

Contributor
Posts: 22

Re: Array using variable for subscript

Thanks for sharing this function with me. Haven't tried it so far but reading the SAS page related to it, I suppose that it doesn't work for my particular case:
Restriction The value of the specified expression cannot denote an array reference.
Super User
Posts: 19,772

Re: Array using variable for subscript

No, your use case doesn't make sense to be honest. If the values are fixed, is there a reason you're not using a temporary array. 

 

If you want to solve this problem for learning, feel free, but posting what you're trying to do will help get you an optimal solution. 

Contributor
Posts: 22

Re: Array using variable for subscript

OK, once again connected and having accessible my code, I'll post the complete program with a description of what I am pretending to solve.
Solution
‎02-03-2017 07:43 PM
Super User
Super User
Posts: 7,039

Re: Array using variable for subscript

You reference members of an array with an integer index into the array. Not with a NAME.  If you want to reference a variable by name then just reference the variable, there is no need to use an array.  

 

If you need to convert a character variable with the name of a variable into the value of the variable the most direct way is to use the VVALUEX() function, but that would return the value as a character string.  If you wanted the value converted back to a number then use INPUT() function.

length vw audi 8 make $32 ;
array brand vw audi (50.5 25.4);
model = 'VW';
mpg = input(vvaluex(model),32.);

Or you could find a method to convert the name to the index to use with the array.  For example using the WHICHC() function might work.

 

model = 'VW';
mpg = brand(whichc(model,'VW','AUDI'));

Try this example:

data _null_;
  length vw audi 8 make $32 ;
  array brand vw audi (50.5 25.4);
  input make ;
  mpg1 = input(vvaluex(make),32.);
  mpg2 = brand(whichc(make,'VW','AUDI'));
  put (_all_) (=);
cards;
VW
AUDI
;;;;
Contributor
Posts: 22

Re: Array using variable for subscript

Thanks to all for the many solutions that were provided.

 

I should explain what I pretended with the array reference.

 

I create a macro variable that holds the valid working days for 2017. In the real example it's more straightforward than only taking into account the weekdays. Here, for the purpose of my problem, I leave it simplified like this.  

  

 

DATA _NULL_;

FORMAT DAYS_STRING $10000.;

do MTuWThF = '01Jan2017'd to '31Dec2017'd;

if weekday(MTuWThF) in (2:6) then DO;

DAYS_STRING=CATX(" ", DAYS_STRING, MTuWThF);

OUTPUT;

END;

end ;

CALL SYMPUTX("WDAYS", DAYS_STRING);

RUN;

 

/* Furthermore I create a dataset with the committee agenda for each brand */

 

 

data COMMITTEES ;

input MAKE $ COMMITTEE date9. ROUND $ ;

format COMMITTEE weekdate17. ;

cards ;

VW 12APR2017 1ST

LCV 19APR2017 1ST

SKODA 24MAY2017 1ST

AUDI 12JUL2017 1ST

SEAT 14JUN2017 1ST

VW 30OCT2017 2ND

LCV 25OCT2017 2ND

SKODA 28NOV2017 2ND

AUDI 20DEC2017 2ND

SEAT 15NOV2017 2ND

;

 

/* Now I want roll back the planning for the prior tasks that lead to each committee. Each brand has different aspects to take into account so that I need to customize the days which are necessary to lay the groundwork for the next event.

I.e. there has to be a time window of 5 days between the internal committee and the final committee.

As the workload and other parameters differ from the first semester to the second, we even have to distinguish between 1st and 2nd semester and fix the dates accordingly.

 

This makes a case for the use of a multi-dimensional array.

1st dim is the semester,

the second for the 5 brands

and the third for the 3 meetings to be scheduled.

 

As I had mentioned before, I created a if-then workaround for getting an integer index that I can throw into the array.

 

The DO LOOP goes back in the calendar until the specified amount of available working days equals the desired number of days between the event and the pre-fixed  committee.  

-------------------------------------------------------------------------------------------------------------------------------------------

 

And Tom's nice code, which I tested successfully, would make the program run more efficiently .  

 

Allthough the program now runs like I expect it to do,

I welcome any suggestion that lets me see where I over-complicate things.

I want to learn from the experts.

 

For example I asked myself if I can declare the multi-dim array with a mixture of integer / intervals and names for the elements like

 

ARRAY CUST {"1st" "2nd", 5, 3} _TEMPORARY_

(26 32 37

8 13 18

5 10 15

5 10 15

5 10 15

22 60 65

12 17 22

15 20 28

8 13 18

10 15 20);

 

*/

 

 

 

 

DATA SCHEDULED;

SET WORK.COMMITTEES;

 

FORMAT INT_COMMITTEE PRE_COMMITTEE PREPARATION weekdate17.;

 

ARRAY ACTIVITY (3) $ INT_COMMITTEE PRE_COMMITTEE PREPARATION;

ARRAY BRAND {*} VW SKODA LCV AUDI SEAT (1 2 3 4 5);

 

ARRAY CUST {2, 5, 3} _TEMPORARY_

(26 32 37

8 13 18

5 10 15

5 10 15

5 10 15

22 60 65

12 17 22

15 20 28

8 13 18

10 15 20);

RETAIN C 0 N 0;

 

IF STRIP(MAKE)="VW" THEN H1=1;

ELSE IF STRIP(MAKE)="SKODA" THEN H1=2;

ELSE IF STRIP(MAKE)="LCV" THEN H1=3;

ELSE IF STRIP(MAKE)="AUDI" THEN H1=4;

ELSE H1=5;

 

IF STRIP(ROUND)="1ST" THEN D1=1;

ELSE D1=2;

 

DO I=1 TO DIM(ACTIVITY);

C=0;

N=0;

DO WHILE (N LT CUST(D1, BRAND(H1),I));

C+1;

IF (COMMITTEE-C) IN (&wdays) THEN N+1;

END;

ACTIVITY(I)=COMMITTEE - C;

END;

KEEP MAKE COMMITTEE INT_COMMITTEE PRE_COMMITTEE PREPARATION;

RUN;

☑ This topic is solved.

Need further help from the community? Please ask a new question.

Discussion stats
  • 11 replies
  • 570 views
  • 4 likes
  • 5 in conversation