Context:
I'm trying to organize blocks of HTML text so that they appear to evenly spread across an element, while keeping their original size (i.e. the size of the string).
Problem:
I have an array of strings and I want to sort them with the given conditions:
- Each run of strings should have it's length no longer than W
- The length of a run is the sum of it's strings' lengths
- Each run should be roughly equal in size
- i.e. the length should be closer to the median length of all runs than to the maximum allowed
- The median length of a run should be as close to the maximum as possible
Bonus:
Each string has a color that constrains where it can be placed. i.e. all blues must be adjacent to each other and cannot be placed randomly throughout. This means that at the head/tail ends of a run of colors must "align" with the next color.
(I think my greedy approach below satisfies this, but fails to make the rows of a similar length)
2x Bonus (easier/harder?):
Allow for runs of colors to be interrupted, but each run must be at least K long, the last run of a color can be smaller than K
Solution can be in any (programming) language, I just put JS cause that's what I'm working with.
Example:
> f(S=['abcd', 'def', 'a', 'b', 'wx', 'dsaad'], W=5)
=> [
'dsaad',
'abcd',
'def', 'b', // length of 4 is better than 5, then 3
'wx', 'a', // length of 3 is better than 2
]
Here is my greedy solution, if nothing else I'd appreciate some advice on optimizations.
interface Tag { name: string; color: string}
const greedyTagSort = (arr: Tag[], width: number) => {
const sorted: Tag[] = [];
const copy = [...arr];
let combined = 0;
let color = '';
const PADDING = 2;
while (sorted.length !== arr.length) {
let max, maxC, maxW, maxWC;
max = maxC = maxW = maxWC = copy[0];
let iMax, iMaxC, iMaxW, iMaxWC;
iMax = iMaxC = iMaxW = iMaxWC = 0;
let i = 0;
while (i < copy.length) {
const tmp = copy[i];
const weightCheck = tmp.name.length + PADDING + combined <= width;
if (color === tmp.color) {
if ((maxWC.color !== color || tmp.name.length > maxWC.name.length) && weightCheck) {
maxWC = tmp;
iMaxWC = i;
} else if (maxC.color !== color || tmp.name.length > maxC.name.length) {
maxC = tmp;
iMaxC = i;
}
}
if (tmp.name.length > maxW.name.length && weightCheck) {
maxW = tmp;
iMaxW = i;
}
if (tmp.name.length > max.name.length) {
max = tmp;
iMax = i;
}
i++;
}
if (color === maxWC.color && maxWC.name.length + PADDING + combined <= width) {
max = maxWC;
iMax = iMaxWC;
} else if (color === maxC.color) {
max = maxC;
iMax = iMaxC;
combined = 0;
} else if (maxW.name.length + PADDING + combined <= width) {
max = maxW;
iMax = iMaxW;
} else {
combined = 0;
}
sorted.push(...copy.splice(iMax, 1));
combined += max.name.length + PADDING;
color = max.color;
}
return sorted;
};
Thanks in advance