Home > Mobile >  How to distribute an array accoding to specific conditions in Google Apps Script?
How to distribute an array accoding to specific conditions in Google Apps Script?

Time:11-03

I'm trying to organize an array I have according to certain conditions that must be met.

I have 3 kinds of arrays: Array of people

var array1 = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]

Array of dates

var array2 = ["date1","date2","date3","date4"]

For each value in array2, I have corresponding separate arrays (or 2D arrays, don't know what's best to be honest)

var date1Group1 = [1,6,7,8]
var date1Group2 = [4,5,9]
var date1Group3 = [6,17,15,3,11]

OR

var date1Groups = [[1,6,7,8],[4,5,9],[6,17,15,3,11]]

Each date has their own corresponding group arrays

var date2Groups = [...]
...
var date4Groups = [...]

What I need to do is to distribute proportionately the values of array1 into a new array corresponding to having it split by array2.length. In this particular case since:

array1.length = 18
array2.length = 4

I can have for example 1 2D array of 2 sub-arrays of 5 values and 2 sub-arrays of 4 values, OR 4 separate arrays for each group:

var arrayResultDate = [[v1,v2,v3,v4,v5],[v1,v2,v3,v4,v5],[v1,v2,v3,v4],[v1,v2,v3,v4]]

OR

var arrayResultDate1 = [v1,v2,v3,v4,v5]
var arrayResultDate2 = [v1,v2,v3,v4,v5]
var arrayResultDate3 = [v1,v2,v3,v4]
var arrayResultDate4 = [v1,v2,v3,v4]

In order to distribute them, I need to check that the values asigned to each of these new arrays, are not repeated in a same group of it's corresponding date. For example:

//This is valid be because no value is repeated in any of the groups for date1
arrayResultDate1 = [2,4,7,14,18]

//This is NOT VALID because values 6 and 8 are in date1Group1 / date1Groups[0]
arrayResultDate1 = [3,6,8,18,12] 

What I wanted to do was first asigning from array1[0] to array1[3], to arrayResultDate1 to arrayResultDate4 (1 each)

arrayResultDate1.push(array1[0])
arrayResultDate2.push(array1[1])
arrayResultDate3.push(array1[2])
arrayResultDate4.push(array1[3])

OR

arrayResultDate[0].push(array1[0])
arrayResultDate[1].push(array1[1])
arrayResultDate[2].push(array1[2])
arrayResultDate[3].push(array1[3])

Then use loops to go through the rest of the values in array1, and check if the criteria is met in each resulting array. Asing to the array if conditions are met or go for the next one if it doesn't.

Conditions: If trying to asign array1[x] to arrayResultDate1, check if value array1[x] AND values already in arrayResultDate1, are at the same time in any of the groups for date1:

date1Group1 = [1,6,7,8]
date1Group2 = [4,5,9]
date1Group3 = [6,17,15,3,11]

If said values ARE NOT at the same time in any of the groups, then

arrayResultDate1.push(array1[x])

If the values ARE in at least one of the groups, then continue with the next resulting array and check with it's corresponding groups (in this case it would be arrayResultDate2 so date2Group1, date2Group2, etc.)

I wasn't able to organize the code properly due to the loops inside other loops that I assume are needed. I hope this is a bit more clear. Please let me know if it's still not.

CodePudding user response:

Use Array.filter() and Array.sort(), like this:

function test() {
  const people = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,];
  const criteria = [
    { label: 'date1', mutuallyExclusive: [[1, 6, 7, 8,], [4, 5, 9,], [6, 17, 15, 3, 11,],], },
    { label: 'date2', mutuallyExclusive: [[1, 6,],], },
    { label: 'date3', mutuallyExclusive: [[1, 7,],], },
    { label: 'date4', mutuallyExclusive: [[1, 8,],], },
  ];
  const assignments = distributeObjectsToGroups(people, criteria);
  console.log(assignments);
}


/**
* Distributes objects to groups observing multiple mutual exclusivity criteria.
*
* Processes each object in turn in the order they appear in the objects array.
* Attempts to make result groups approximately equal size by placing each object
* in the currently smallest group.
* Puts objects that do not fit in any group in an 'unassigned' group.
*
* @param {Object[]} objects The objects to place into groups, as in [1, 2, 3, 4,].
* @param {Object[]} groups An array of group labels and mutual exclusivity criteria.
*                   Each criteria is a 2D array that lists objects that should be 
*                   mutually exclusive and not appear in the same result group.
*                   [
*                     { label: 'date1', mutuallyExclusive: [[1, 2, 3,], [2, 4,]], },
*                     { label: 'date2', mutuallyExclusive: [[2, 3,],], },
*                   ]
* @return {Object}  The group labels and objects placed in each group.
*                   { date1: [1, 4,], date2: [2,], unassigned: [3,], }
*/
function distributeObjectsToGroups(objects, groups) {
  // version 1.1, written by --Hyde, 3 November 2022
  //  - see https://stackoverflow.com/a/74294212/13045193
  const result = { unassigned: [], };
  groups.forEach(group => result[group.label] = []);
  objects.forEach(object => {
    const suitable = groups
      .filter(group =>
        !group.mutuallyExclusive.some(exclude =>
          exclude.includes(object) &&
          result[group.label].some(object2 => exclude.includes(object2)))
      )
      .sort((a, b) => result[a.label].length - result[b.label].length);
    if (suitable.length) {
      result[suitable[0].label].push(object);
      return;
    }
    result.unassigned.push(object);
  });
  return result;
}

A test run logs this:

{
  date1: [1, 5, 10, 13, 17],
  date2: [2, 6, 9, 14, 18],
  date3: [3, 7, 11, 15],
  date4: [4, 8, 12, 16],
  unassigned: [],
}

See Apps Script at Stack Overflow.

  • Related