Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Index of string value in MiniZinc array

The question

Given a MiniZinc array of strings:

int: numStats;
set of int: Stats = 1..numStats;
array[Stats] of string: statNames;

... with data loaded from a MiniZinc data file:

numStats = 3;
statNames = ["HEALTH", "ARMOR", "MANA"];

How can one look up the index of a specific string in the array? For example, that ARMOR is located at position 2.

The context

I need to find an optimal selection of items with regard to some constraints on their stats. This information is stored in a 2D array declared as follows:

int: numItems;
set of int: Items = 1..numItems;
array[Items, Stats] of float: itemStats;

So in order to write a constraint on, say, the minimum amount of ARMOR obtained through the selected items, I need to know that ARMOR has index 2 in the inner array.

Since the data file is generated by an external program, and the number and order of stats are dynamic, I cannot hardcode the indices in the constraints.

One solution (that won't work in my case)

The MiniZinc tutorial uses an interesting trick to achieve something similar:

set of int: Colors = 1..3;
int: red = 1;
int: yellow = 2;
int: blue = 3;
array[Colors] of string: name = ["red", "yellow", "blue"];

var Colors: x;
constraint x != red;
output [ name[fix(x)] ];

Unfortunately, as variable declarations are not allowed in MiniZinc data files, this trick won't work in my case.

like image 602
Zero3 Avatar asked Sep 02 '25 01:09

Zero3


1 Answers

You can write your own custom function to get the index of a string within a string array:

function int: getIndexOfString(string: str, 
                               array[int] of string: string_array) = 
   sum(  [ if str = string_array[i] 
              then i
           else 0 endif  
          | i in index_set(string_array) ]
   );

In this function I create an array of integers where the integer at position i either equals the index of str if string_array[i]=str and 0 otherwise. For instance, for your sample string array ["HEALTH", "ARMOR", "MANA"] and str ARMOR the resulting int array will be [0,2,0].

This is why I can simply sum over the int array to get the index of the string. If the string does not occur, the return value is 0, which is fine since indices in MiniZinc start with 1 by default.

Here is how you can call the function above for your first example:

int: numStats;
set of int: Stats = 1..numStats;
array[Stats] of string: statNames;

numStats = 3;
statNames = ["HEALTH", "ARMOR", "MANA"];

var int: indexOfArmor;

constraint 
   indexOfArmor = getIndexOfString("ARMOR",statNames);  

solve satisfy;  

Note however that the function above is limited and has some flaws. First, if you have multiple occurrences of the string in the array, then you will receive an invalid index (the sum of all indices where str occurred). Also, if you have your own index set for your string array (say (2..6)), then you will need to adapt the function.

like image 171
Andrea Rendl-Pitrey Avatar answered Sep 04 '25 23:09

Andrea Rendl-Pitrey