This is what is knows as The Bin Packing Problem
Solutions will depend on your definition of efficient, more efficient bin packing tends to take longer to compute. But less efficient bin packing tends to take much less computational resources (could be deemed more efficient in terms of computational resources).
Here is my solution using ECMA5 specifications, it sacrifices computational resources in favour of more efficient bin packing.
I have chosen to leave the powerSet
function a general purpose one, that means it can be reused for other purposes. As @Bergi has highlighted, you may get better efficiency with respect to computational resources if you created a custom function which incorporates the first filter
from joinStringsMaxCharacters
Javascript
General Power Set
function
function powerSet(array) {
var lastElement,
sets;
if (!array.length) {
sets = [[]];
} else {
lastElement = array.pop();
sets = powerSet(array).reduce(function (previous, element) {
previous.push(element);
element = element.slice();
element.push(lastElement);
previous.push(element);
return previous;
}, []);
}
return sets;
}
Helper functions
function isString(element) {
return typeof element === 'string';
}
function isNotUsed(element) {
return this.every(function (u) {
return u.indexOf(element) === -1;
});
}
function sumLength(s, el) {
return s + el.length;
}
Packing bins with joined strings
function joinStringsMaxCharacters(arrayOfStrings, numberOfCharacters, separator) {
if (!Array.isArray(arrayOfStrings) || !arrayOfStrings.every(isString)) {
throw new TypeError('arrayOfStrings is not an array of strings!');
}
numberOfCharacters = numberOfCharacters >>> 0;
if (!separator || !isString(separator)) {
separator = '|';
}
var arrayLength = arrayOfStrings.length;
return powerSet(arrayOfStrings).filter(function (set) {
return set.length && (set.length === 1 || set.reduce(sumLength, set.length - 1) <= numberOfCharacters);
}).sort(function (a, b) {
return b.reduce(sumLength, b.length) - a.reduce(sumLength, a.length) || b.length - a.length;
}).reduce(function (used, cur) {
if (used.reduce(sumLength, 0) < arrayLength && cur.every(isNotUsed, used)) {
used.push(cur);
}
return used;
}, []).map(function (bin) {
return bin.join(separator);
});
}
Array of strings to be packed into bins
var nameList = [
"Andrew Alexander Brown",
"Charlie Christopher Drake",
"Edward Elsevier Furlong",
"Gareth Gates Harper",
"Indiana Chewbacca Jones",
"Kevin M Lamarr",
"Michael Randy Newman",
"Oliver Terry Pratchett",
"Queen Liz Regina",
"Stuart Townsend",
"Umar Vespa",
"Woodford X Xanadu",
"Yanick Zebra"];
Pack the bins and display the content of each bin
joinStringsMaxCharacters(nameList, 48).forEach(function (bin) {
console.log(bin);
});
Output
Kevin M Lamarr|Stuart Townsend|Woodford X Xanadu Michael Randy Newman|Queen Liz Regina|Umar Vespa Charlie Christopher Drake|Oliver Terry Pratchett Edward Elsevier Furlong|Indiana Chewbacca Jones Andrew Alexander Brown|Gareth Gates Harper Yanick Zebra
On jsFiddle