I'm trying to do a macro that will sort some text according to some text. Can anyone provide a macro that will do below?
This is what I would like it to do step by step:
Ask me to input the text to sort by (but with a default value already inserted). This will always be comma separated, for example "Common Nighthawk,Chimney Swift,Yellow-billed Cuckoo,Black-billed Cuckoo".
Use text I have selected in some other app as input. Again this will be always be comma separated, for example "Black-billed Cuckoo,Chimney Swift,Yellow-billed Cuckoo,Common Nighthawk"
Replace the selected text with the sorted text. In the above examples, the selected text "Black-billed Cuckoo,Chimney Swift,Yellow-billed Cuckoo,Common Nighthawk" will be replaced with "Common Nighthawk,Chimney Swift,Yellow-billed Cuckoo,Black-billed Cuckoo".
Should generally be overlap between pre-ordered list and selected list but in some cases none of pre-ordered list will be in selected list, and none of selected list will be in pre-ordered list
When items in pre-ordered list are not found in selected list, then still reorder items in selected list in the order they appear in pre-ordered list
pre-ordered list will match selected list including characters and cases
Yellow-billed Cuckoo and Black-billed Cuckoo always hyphenated
return (() => {
"use strict";
// kmvar.local_CopiedCSV sorted by custom order specified in
// kmvar.local_PreOrderedCSV
// Items not found in the custom ordering are pushed to the end.
// main :: IO ()
const main = () => {
const
rgxComma = /\s*,\s*/gu,
keyOrder = kmvar.local_PreOrderedCSV
.split(rgxComma)
.reduce(
(a, k, i) => Object.assign(
a,
{ [canonical(k)]: 1 + i }
),
{}
);
return sortOn(
k => keyOrder[canonical(k)] || Infinity
)(
kmvar.local_CopiedCSV.split(rgxComma)
)
.join(",");
};
// canonical :: String -> String
const canonical = k =>
k.split(/\W/u)
.map(toLower)
.join("");
// --------------------- GENERIC ---------------------
// comparing :: Ord a => (b -> a) -> b -> b -> Ordering
const comparing = f =>
// The ordering of f(x) and f(y) as a value
// drawn from {-1, 0, 1}, representing {LT, EQ, GT}.
x => y => {
const
a = f(x),
b = f(y);
return a < b
? -1
: a > b
? 1
: 0;
};
// sortBy :: (a -> a -> Ordering) -> [a] -> [a]
const sortBy = f =>
// A copy of xs sorted by the comparator function f.
xs => xs.slice()
.sort((a, b) => f(a)(b));
// sortOn :: Ord b => (a -> b) -> [a] -> [a]
const sortOn = f =>
// Equivalent to sortBy(comparing(f)), but with f(x)
// evaluated only once for each x in xs.
// ('Schwartzian' decorate-sort-undecorate).
xs => sortBy(
comparing(x => x[0])
)(
xs.map(x => [f(x), x])
)
.map(x => x[1]);
// toLower :: String -> String
const toLower = s =>
// Lower-case version of string.
s.toLocaleLowerCase();
return main();
})();
That's amazing thanks! For any birds in local_CopiedCSV not present in local_PreOrderedCSV, can they be excluded from local_CopiedCSV and outputted separately, say in an alert pop up?
return (() => {
"use strict";
// kmvar.local_CopiedCSV sorted by custom order specified in
// kmvar.local_PreOrderedCSV
// Items not found in the custom ordering are pushed to the end.
// main :: IO ()
const main = () => {
const
rgxComma = /\s*,\s*/gu,
commaSeparated = intercalate(","),
keyOrder = kmvar.local_PreOrderedCSV
.split(rgxComma)
.reduce(
(a, k, i) => Object.assign(
a,
{ [canonical(k)]: 1 + i }
),
{}
),
[sorted, extras] = bimap(
compose(
commaSeparated,
sortOn(
k => keyOrder[canonical(k)] || Infinity
)
)
)(
commaSeparated
)(
partition(
k => canonical(k) in keyOrder
)(
kmvar.local_CopiedCSV.split(rgxComma)
)
);
return {
sorted,
extras
};
};
// canonical :: String -> String
const canonical = k =>
k.split(/\W/u)
.map(toLower)
.join("");
// --------------------- GENERIC ---------------------
// Tuple (,) :: a -> b -> (a, b)
const Tuple = a =>
// A pair of values, possibly of
// different types.
b => ({
type: "Tuple",
"0": a,
"1": b,
length: 2,
*[Symbol.iterator]() {
for (const k in this) {
if (!isNaN(k)) {
yield this[k];
}
}
}
});
// bimap :: (a -> b) -> (c -> d) -> (a, c) -> (b, d)
const bimap = f =>
// Tuple instance of bimap.
// A tuple of the application of f and g to the
// first and second values respectively.
g => tpl => Tuple(f(tpl[0]))(
g(tpl[1])
);
// comparing :: Ord a => (b -> a) -> b -> b -> Ordering
const comparing = f =>
// The ordering of f(x) and f(y) as a value
// drawn from {-1, 0, 1}, representing {LT, EQ, GT}.
x => y => {
const
a = f(x),
b = f(y);
return a < b
? -1
: a > b
? 1
: 0;
};
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
const compose = (...fs) =>
// A function defined by the right-to-left
// composition of all the functions in fs.
fs.reduce(
(f, g) => x => f(g(x)),
x => x
);
// first :: (a -> b) -> ((a, c) -> (b, c))
const first = f =>
// A simple function lifted to one which applies
// to a tuple, transforming only its first item.
([x, y]) => Tuple(f(x))(y);
// intercalate :: String -> [String] -> String
const intercalate = s =>
// The concatenation of xs
// interspersed with copies of s.
xs => xs.join(s);
// partition :: (a -> Bool) -> [a] -> ([a], [a])
const partition = p =>
// A tuple of two lists - those elements in
// xs which match p, and those which do not.
xs => [...xs].reduce(
(a, x) => (
p(x)
? first
: second
)(ys => [...ys, x])(a),
Tuple([])([])
);
// second :: (a -> b) -> ((c, a) -> (c, b))
const second = f =>
// A function over a simple value lifted
// to a function over a tuple.
// f (a, b) -> (a, f(b))
xy => Tuple(
xy[0]
)(
f(xy[1])
);
// sortBy :: (a -> a -> Ordering) -> [a] -> [a]
const sortBy = f =>
// A copy of xs sorted by the comparator function f.
xs => xs.slice()
.sort((a, b) => f(a)(b));
// sortOn :: Ord b => (a -> b) -> [a] -> [a]
const sortOn = f =>
// Equivalent to sortBy(comparing(f)), but with f(x)
// evaluated only once for each x in xs.
// ('Schwartzian' decorate-sort-undecorate).
xs => sortBy(
comparing(x => x[0])
)(
xs.map(x => [f(x), x])
)
.map(x => x[1]);
// toLower :: String -> String
const toLower = s =>
// Lower-case version of string.
s.toLocaleLowerCase();
return JSON.stringify(main(), null, 2);
})();