Here is an approach which looks up the product in a MultiMarkdown table.

The table doesn't have to be neat, as long as:

- There is an MMD table 'ruler' in the second line,
- and a pipe character between each field, and
- the product name is spelled exactly as it will be searched for.

(spaces to left and right of values will be ignored,

as will any pipe character at start and end of of each row );

You could also, of course, keep the MMD table in a text file, and get KM to read it in.

Lookup MMD table and set variables by product key.kmmacros (23.8 KB)

**Javascript source:**

```
(() => {
'use strict';
const main = () => {
const
kme = Application('Keyboard Maestro Engine'),
strTable = kme.getvariable('mmdTable'),
strProduct = kme.getvariable('someProduct'),
dctMatch = dictFromMMDTable(strTable)[strProduct];
return dctMatch !== undefined ? (
Object.keys(dctMatch).forEach(
k => kme.setvariable(k, {
to: dctMatch[k]
})
),
'Variables set: ' + showJSON(dctMatch, null)
) : 'Product not found in table as spelled: ' + strProduct;
};
// DICT FROM MMD TABLE ---
// dictFromMMDTable :: String -> Dict
const dictFromMMDTable = strTable => {
const
rgxRuler = /^[\s\|\:\-\.]+$/,
notRuler = x => !rgxRuler.test(x),
notEmpty = x => x.trim().length > 0,
// Any pipe(s) at start or end of row are ignored.
values = s => map(
strip,
filter(notEmpty, s.split(/\s*\|\s*/))
),
xs = filter(notEmpty, lines(strTable)),
colNames = values(takeWhile(notRuler, xs)[0]),
rows = map(values, takeWhileR(notRuler, xs));
return foldl(
(a, vs) => {
const kvs = zip(colNames, vs);
return Object.assign(
a, {
// The value in the first column of a row
// is used in the top-level table dict
// as a key to a new row dict.
[kvs[0][1]]: foldl((subDict, tpl) =>
// Each cell is added under a colName key
// to the dict representing its row.
Object.assign(
subDict, {
[tpl[0]]: tpl[1]
}
), {}, kvs.slice(1))
}
);
}, {},
rows
);
};
// GENERIC FUNCTIONS --------------------------------------
// Tuple (,) :: a -> b -> (a, b)
const Tuple = (a, b) => ({
type: 'Tuple',
'0': a,
'1': b,
length: 2
});
// filter :: (a -> Bool) -> [a] -> [a]
const filter = (f, xs) => xs.filter(f);
// foldl :: (a -> b -> a) -> a -> [b] -> a
const foldl = (f, a, xs) => xs.reduce(f, a);
// lines :: String -> [String]
const lines = s => s.split(/[\r\n]/);
// map :: (a -> b) -> [a] -> [b]
const map = (f, xs) => xs.map(f);
// showJSON :: a -> String
const showJSON = x => JSON.stringify(x, null, 2);
// strip :: String -> String
const strip = s => s.trim();
// takeWhile :: (a -> Bool) -> [a] -> [a]
const takeWhile = (p, xs) => {
let i = 0,
lng = xs.length;
while ((i < lng) && p(xs[i])) {
i = i + 1;
}
return xs.slice(0, i);
};
// takeWhileR :: (a -> Bool) -> [a] -> [a]
const takeWhileR = (p, xs) => {
let i = xs.length;
while (i-- && p(xs[i])) {}
return xs.slice(i + 1);
};
// zip :: [a] -> [b] -> [(a, b)]
const zip = (xs, ys) =>
xs.slice(0, Math.min(xs.length, ys.length))
.map((x, i) => Tuple(x, ys[i]));
// MAIN ---
return main();
})();
```