JavaScript function that returns a sorted array of objects ordered by properties and orders.

/**
 * order By
 *
 * Returns a sorted array of objects ordered by properties and orders.
 *
 * Returns a sorted array of objects ordered by properties and orders.
 * Uses `Array.prototype.sort()`, `Array.prototype.reduce()` on the props array
 * with a default value of 0, use array destructuring to swap the properties
 * position depending on the order passed. If no `orders` array is passed it
 * sort by `'asc'` by default.
 *
 * @param {Array} arr
 * @param {Array} props
 * @param {Array} orders
 * @return {Array} A sorted array of objects ordered by properties and orders.
 */
const orderBy = (arr, props, orders) =>
    [...arr].sort((a, b) =>
        props.reduce((acc, prop, i) => {
            if (acc === 0) {
                const [p1, p2] =
                    orders && orders[i] === "desc"
                        ? [b[prop], a[prop]]
                        : [a[prop], b[prop]];
                acc = p1 > p2 ? 1 : p1 < p2 ? -1 : 0;
            }
            return acc;
        }, 0)
    );

//
// Compiled (ES2015)
var _slicedToArray = (function() {
    function sliceIterator(arr, i) {
        var _arr = [];
        var _n = true;
        var _d = false;
        var _e = undefined;
        try {
            for (
                var _i = arr[Symbol.iterator](), _s;
                !(_n = (_s = _i.next()).done);
                _n = true
            ) {
                _arr.push(_s.value);
                if (i && _arr.length === i) break;
            }
        } catch (err) {
            _d = true;
            _e = err;
        } finally {
            try {
                if (!_n && _i["return"]) _i["return"]();
            } finally {
                if (_d) throw _e;
            }
        }
        return _arr;
    }
    return function(arr, i) {
        if (Array.isArray(arr)) {
            return arr;
        } else if (Symbol.iterator in Object(arr)) {
            return sliceIterator(arr, i);
        } else {
            throw new TypeError("Invalid attempt to destructure non-iterable instance"
            );
        }
    };
})();

function _toConsumableArray(arr) {
    if (Array.isArray(arr)) {
        for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {
            arr2[i] = arr[i];
        }
        return arr2;
    } else {
        return Array.from(arr);
    }
}

var orderBy = function orderBy(arr, props, orders) {
    return [].concat(_toConsumableArray(arr)).sort(function(a, b) {
        return props.reduce(function(acc, prop, i) {
            if (acc === 0) {
                var _ref =
                        orders && orders[i] === "desc"
                            ? [b[prop], a[prop]]
                            : [a[prop], b[prop]],
                    _ref2 = _slicedToArray(_ref, 2),
                    p1 = _ref2[0],
                    p2 = _ref2[1];

                acc = p1 > p2 ? 1 : p1 < p2 ? -1 : 0;
            }
            return acc;
        }, 0);
    });
};

// --------------------------------------------------
// Example Usage
// --------------------------------------------------

const users = [
    { name: "fred", age: 48 },
    { name: "barney", age: 36 },
    { name: "fred", age: 40 }
];

orderBy(users, ["name", "age"], ["asc", "desc"]); // [{name: 'barney', age: 36}, {name: 'fred', age: 48}, {name: 'fred', age: 40}]
orderBy(users, ["name", "age"]); // [{name: 'barney', age: 36}, {name: 'fred', age: 40}, {name: 'fred', age: 48}]