Samuel Charpentier
Samuel Charpentier

Reputation: 609

How to get objects ordered by attribute from different arrays?

I have 2 arrays containing objects and I want to make an array with all objects that fits a condition, ordered by one attribute.

These are my arrays

var mandatorySections = [
    {ordered_position:0,loaded:false},
    {ordered_position:2,loaded:false},
    {ordered_position:4,loaded:true}
    {ordered_position:8,loaded:false}],
optionnalSections = [{ordered_position:3,loaded:false}];

And I want one array containing all objects that haven't been loaded, in ordered_position order, like this:

var result = [
    {ordered_position:0,loaded:false},
    {ordered_position:2,loaded:false},
    {ordered_position:3,loaded:false},
    {ordered_position:8,loaded:false}];

Upvotes: 0

Views: 53

Answers (3)

nitobuendia
nitobuendia

Reputation: 1248

You can achieve this with 3 built-in array features: concat, filter and sort.

Note: I have built this code assuming that you want to change the conditions by which to filter and or sort. Hence, I split all in different functions, but if the conditions are always the same, you could simplify the main function.

First of all, you want to filter. Hence, we need a function to return true when it meets the conditions for which you want to keep the element and false for the ones which you do not want:

/**
 * Function defined to decide how to filter our objects.
 * @param {!Object} element - Object to filter.
 * @return {boolean} Whether it meets all the conditions.
 */
var filterFunction = function(element) {
  // Define all conditions by which to filter. 
  return element.loaded === false;
  /*
   * Other example: return objects whose attributes meet these conditions:
   * ordered_position lesser than five
   * AND loaded === true;
   *
   * return element.ordered_position <= 5 && element.loaded;
   */
}

Following your example, this one will only include elements whose attribute loaded is false. You could add more conditions or change them at your will for each execution.

Then, we need a sorting function:

/**
 * Function to order objects by parameters.
 * @param {Object} a - First object in comparison.
 * @param {Object} b - Second object in comparison.
 * @return {number} Order: negative (< 0), positive (> 0) or neutral (0).
 */
var orderFunction = function(a, b) {
  // Order by ordered_position ASC.
  return a.ordered_position - b.ordered_position;
}

This one orders by ordered_position ASC as if the previous value (a) > next value (b) it returns a positive value. You could do DESC by reversing the parameters or build your own sorting function for ordering a string or even order by more than one parameter.

Finally we can put together our workflow:

/**
 * Gets all the arrays, filters by one parameter,
 * returns data ordered by another parameter as an Array.
 * @param {!Array<Array>} arrays - Group of arrays to search in.
 * @param {Function} opt_filterBy - Function with filtering conditions.
 * @param {Function} opt_orderBy - Parameter for which to order.
 * @return {Array<Object>} Merged, filtered and sorted Array.
 */
var arrayFilter = function (arrays, opt_filterBy, opt_orderBy) {
  // Merge all arrays in one.
  var mergedArray = [].concat.apply([], arrays);
  // Iterate array to remove elements.
  if (opt_filterBy) var filteredArray = mergedArray.filter(opt_filterBy);
  // Sort array by parameter.
  if (opt_orderBy) filteredArray = filteredArray.sort(opt_orderBy);
  // return Merged, filtered and sorted Array.
  return filteredArray;
}

The function takes 3 parameters: your array of Arrays (with all the arrays you want to merge, filter and sort), a function to filter (optional, the first one we defined) and a function to sort (optional, the second one we defined).

To apply this to your case, you would run this:

var mandatorySections = [
  {ordered_position:0,loaded:false},
  {ordered_position:2,loaded:false},
  {ordered_position:4,loaded:true},
  {ordered_position:8,loaded:false}
];

var optionnalSections = [
  {ordered_position:3,loaded:false}
];

arrayFilter(
  [mandatorySections, optionnalSections],
  filterFunction,
  orderFunction
);

/**
 * Function defined to decide how to filter our objects.
 * @param {!Object} element - Object to filter.
 * @return {boolean} Whether it meets all the conditions.
 */
var filterFunction = function(element) {
  // Define all conditions by which to filter. 
  return element.loaded === false;
  /*
   * Other example: return objects whose attributes meet these conditions:
   * ordered_position lesser than five
   * AND loaded === true;
   *
   * return element.ordered_position <= 5 && element.loaded;
   */
}


/**
 * Function to order objects by parameters.
 * @param {Object} a - First object in comparison.
 * @param {Object} b - Second object in comparison.
 * @return {number} Order: negative (< 0), positive (> 0) or neutral (0).
 */
var orderFunction = function(a, b) {
  // Order by ordered_position ASC.
  return a.ordered_position - b.ordered_position;
}


/**
 * Gets all the arrays, filters by one parameter,
 * returns data ordered by another parameter as an Array.
 * @param {!Array<Array>} arrays - Group of arrays to search in.
 * @param {Function} opt_filterBy - Function with filtering conditions.
 * @param {Function} opt_orderBy - Parameter for which to order.
 * @return {Array<Object>} Merged, filtered and sorted Array.
 */
var arrayFilter = function (arrays, opt_filterBy, opt_orderBy) {
  // Merge all arrays in one.
  var mergedArray = [].concat.apply([], arrays);
  // Iterate array to remove elements.
  if (opt_filterBy) var filteredArray = mergedArray.filter(opt_filterBy);
  // Sort array by parameter.
  if (opt_orderBy) filteredArray = filteredArray.sort(opt_orderBy);
  // return Merged, filtered and sorted Array.
  return filteredArray;
}




/* Execution of code. */
var mandatorySections = [
  {ordered_position:0,loaded:false},
  {ordered_position:2,loaded:false},
  {ordered_position:4,loaded:true},
  {ordered_position:8,loaded:false}
];

var optionnalSections = [
  {ordered_position:3,loaded:false}
];

var result = arrayFilter(
  [mandatorySections, optionnalSections],
  filterFunction,
  orderFunction
);

console.log(result);

I have tried to keep this general and extensible, but you can easily simplify the code if your use case is more basic.

I hope this helps.

Upvotes: 2

paqash
paqash

Reputation: 2314

var mandatorySections = [
    {ordered_position:0,loaded:false},
    {ordered_position:2,loaded:false},
    {ordered_position:4,loaded:true},
    {ordered_position:8,loaded:false}],
optionnalSections = [{ordered_position:3,loaded:false}];

var arr = mandatorySections.concat(optionnalSections).filter(function(item) {
 return !item.loaded;
}).sort(function(a,b) {
 return a.ordered_position - b.ordered_position})

console.log(arr);

Edit: Explanation:

Concat joins the arrays, filter takes out the loaded items by checking the loaded flag, and sort sorts by ordered_position.

Upvotes: 3

Nina Scholz
Nina Scholz

Reputation: 386634

You could concat the items, filter and sort the result set.

var mandatorySections = [{ ordered_position: 0, loaded: false }, { ordered_position: 2, loaded: false }, { ordered_position: 4, loaded: true }, { ordered_position: 8, loaded: false }],
    optionnalSections = [{ ordered_position: 3, loaded: false }],
    result = mandatorySections.
        concat(optionnalSections).
        filter(function (a) { return !a.loaded; }).
        sort(function (a, b) { return a.ordered_position - b.ordered_position; });

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 2

Related Questions