/**
*
* @class Utility class that provides methods to move Array elements into a specific order.
* @hideconstructor
*
* @copyright (c) 2021 TLF Research Ltd.
*
*/
function Move() { }
/**
* Returns a new array with all the array elements that match the predicate moved to the end of the array.
* Moved elements are appended in the order in which they appear in the original array.
* The original array is unchanged.
*
* @example
* // b becomes [{"prop": "", "val": 1}, {"prop": "2", "val": 4}, {"prop": null, "val": 2}, {"prop": "10", "val": 3}]
* const a = [{"prop": "", "val": 1}, {"prop": null, "val": 2}, {"prop": "10", "val": 3}, {"prop": "2", "val": 4}];
* const b = Move.toEnd(a, el => el.val > 1 && el.val < 4);
* @param {array} arr Array to examine
* @param {function} predicate Filter method that is called for each element in the array. It should return true if the array element is to be moved.
*
* @return {array}
*/
Move.toEnd = function (arr, predicate) {
const stack = [];
const lastIndex = arr.length - 1;
return arr.reduce((acc, a, idx) => {
if (predicate(a)) {
stack.push(a);
} else {
acc.push(a);
}
if (idx === lastIndex) {
acc.push(...stack);
}
return acc;
}, []);
};
/**
* Returns a new array with all all array elements moved into the specified order.
* Elements with identical object property values are moved together i.e retain their original relative order.
* Any unordered values from the original array are appended to the end of the new array.
* The original array is unchanged.
*
* @example
* // b is [{"prop": "", "val": 1}, {"prop": "2", "val": 4}, {"prop": null, "val": 2}, {"prop": "10", "val": 3}]
* const a = [{"prop": "10", "val": 3}, {"prop": "", "val": 1}, {"prop": "2", "val": 4}, {"prop": null, "val": 2}];
* const b = Move.inOrder(a, "val", [3, 1, 4, 2]);
*
* @param {array} arr Array of objects to examine
* @param {string} property Object property to use for the reorder.
* @param {array} valueOrder Desired order of property values.
*
* @return {array}
*/
Move.inOrder = function (arr, property, valueOrder) {
const result = [];
const reordered = [];
valueOrder.forEach(v => {
for (let idx = 0; idx < arr.length; idx++) {
const obj = arr[idx];
// Use == not === here as we want to match numerics with strings and vice-versa
if (typeof obj[property] !== 'undefined' && obj[property] == v) {
result.push(obj);
reordered.push(idx);
}
}
});
// Add any unordered values in the original array to the end of the result
result.push(...arr.filter((v, idx) => reordered.indexOf(idx) === -1));
return result;
}