skip to Main Content

Hei,

I’ve been cracking my head on an JS algorithm exercise where I have two arrays: one with genders and one with numbers determining their age. I need to compare both and determine how many are of one gender above a certain age, and how many are of the opposite gender under a certain age. What would be the best way to implement it? The arrays always have the same length.

Example arrays would be:

gender = ["man","woman","man","man","woman"]
age = [30,20,13,63,9]

I’ve used various for loops in order to determine how many are men and women, as well as over 18 and under.

The for loops were like this:

for(let i = 0; i < age.length; i++) {
  if(age[i] >= 18) {
    adults ++
  } else {
    minors ++
  }
}

where the initial value of adults & minors is 0

I’m considering if I should instead turn them into arrays, and return later the length of the array, since I have to return an amount rather than a list.

5

Answers


  1. You could implement a kind of array zipping with Array.reduce in order to have a more convenient structure to use, and then just filter your objects as you need and get the length of the result:

    const genders = ["man", "woman", "man", "man", "woman"]
    const ages = [30, 20, 13, 63, 9]
    let i = -1;
    const persons = genders.reduce((res, cur) => {
      return [
        ...res,
        {
          gender: cur,
          age: ages[++i]
        }
      ]
    }, []);
    
    console.log("men above 18", persons.filter(p => p.gender === "man" && p.age >= 18).length);
    console.log("women below 18", persons.filter(p => p.gender === "woman" && p.age < 18).length);
    console.log("men below 18", persons.filter(p => p.gender === "man" && p.age < 18).length);
    console.log("women above 18", persons.filter(p => p.gender === "woman" && p.age >= 18).length);
    Login or Signup to reply.
  2. Just counting adults and minors is not enough. You probably need adultMen adultWomen, minorMen and minorWomen. And you need to compare for age and gender.

        let gender = ["man","woman","man","man","woman"]
        let age = [30,20,13,63,9]
        let adultMen = 0, adultWomen = 0, minorMen = 0, minorWomen = 0;
        for (let i = 0; i < age.length; i++) {
          if (gender[i] == "man"){
            if (age[i] < 18) minorMen++;
            else adultMen++;
          } else {
            if (age[i] < 18) minorWomen++;
            else adultWomen++;
          }
        }
        console.log(adultMen, adultWomen, minorMen, minorWomen);
    Login or Signup to reply.
  3. Here’s an O(n) solution using object as the data structure:

    function countByAge(genders, ages, pivot) {
      // Initialize a data object
      const data = {};
    
      for (let i = 0, l = genders.length; i < l; i++) {
        const gender = genders[i];
        const age = ages[i];
        
        // If we encounter a man, create a man object
        // that holds counters iff it hasn't been created already.
        // Same thing for a woman.
        if (!(gender in data)) {
          data[gender] = {
            older: 0,
            equal: 0,
            younger: 0
          };
        }
    
        // Then we simply add one to whichever counter applicable.
        if (age > pivot) {
          data[gender].older++;
        } else if (age === pivot) {
          data[gender].equal++;
        } else {
          data[gender].younger++;
        }
      }
      
      return data;
    }
    

    Try it:

    /* <ignore> */
    console.config({ maximize: true, timeStamps: false, autoScroll: false });
    /* </ignore> */
    
    const genders = ['man', 'woman', 'man', 'woman', 'man', 'woman', 'man'];
    const ages = [30, 20, 13, 18, 63, 9, 18];
    const pivot = 18;
    
    console.log(countByAge(genders, ages, pivot));
    
    function countByAge(genders, ages, pivot) {
      const data = {};
    
      for (let i = 0, l = genders.length; i < l; i++) {
        const gender = genders[i];
        const age = ages[i];
    
        if (!(gender in data)) {
          data[gender] = {
            older: 0,
            equal: 0,
            younger: 0
          };
        }
    
        if (age > pivot) {
          data[gender].older++;
        } else if (age === pivot) {
          data[gender].equal++;
        } else {
          data[gender].younger++;
        }
      }
      
      return data;
    }
    <script src="https://gh-canon.github.io/stack-snippet-console/console.min.js"></script>

    Terser version (0 for younger, 1 for equal, 2 for older):

    /* <ignore> */
    console.config({ maximize: true, timeStamps: false, autoScroll: false });
    /* </ignore> */
    
    const genders = ['man', 'woman', 'man', 'woman', 'man', 'woman', 'man'];
    const ages = [30, 20, 13, 18, 63, 9, 18];
    const pivot = 18;
    
    console.log(countByAge(genders, ages, pivot));
    
    function countByAge(genders, ages, pivot) {
      const data = {};
    
      for (let i = 0, l = genders.length; i < l; i++) {
        const gender = genders[i];
        const age = ages[i];
        
        (data[gender] ??= [0, 0, 0])[Math.sign(age - pivot) + 1]++;
      }
      
      return data;
    }
    <script src="https://gh-canon.github.io/stack-snippet-console/console.min.js"></script>

    …or, if you prefer -1/0/1:

    /* <ignore> */
    console.config({ maximize: true, timeStamps: false, autoScroll: false });
    /* </ignore> */
    
    const genders = ['man', 'woman', 'man', 'woman', 'man', 'woman', 'man'];
    const ages = [30, 20, 13, 18, 63, 9, 18];
    const pivot = 18;
    
    console.log(countByAge(genders, ages, pivot));
    
    function countByAge(genders, ages, pivot) {
      const data = {};
    
      for (let i = 0, l = genders.length; i < l; i++) {
        const gender = genders[i];
        const age = ages[i];
        
        (data[gender] ??= { '-1': 0, 0: 0, 1: 0 })[Math.sign(age - pivot)]++;
      }
      
      return data;
    }
    <script src="https://gh-canon.github.io/stack-snippet-console/console.min.js"></script>
    Login or Signup to reply.
  4. gender = ["man","woman","man","man","woman"];
    age = [30,20,13,63,9];
    var adultmen = age.filter(function(obj,i){
                           return obj >=18 && gender[i]=="man";
                       }).length;
    var minormen = age.filter(function(obj,i){
                           return obj <18 && gender[i]=="man";
                       }).length;;
    var adultwomen = age.filter(function(obj,i){
                           return obj >=18 && gender[i]=="woman";
                       }).length;
    var minorwomen = age.filter(function(obj,i){
                           return obj <18 && gender[i]=="woman";
                       }).length;;
    
    Login or Signup to reply.
  5. I think we can make this more powerful and more useful by abstracting a bit. We have some lists of values associated with a property name ("gender", "age"). Those lists have the same length. We want to query on the combinations of those lists.

    So we can write a generic combine that accepts input like {gender: ['man', 'woman', 'man', 'man', 'woman'], age: [30, 20, 13, 63, 9]} and returns a collection of functions , here just query, and count, but we might imagine others.

    Then we pass a predicate function into either of these and get the matching results or their count. So we might write

    const db = combine({gender: ['man', 'woman', 'man', 'man', 'woman'], age: [30, 20, 13, 63, 9]})
    
    const numberOfAdults = db .count (p => p .age >= 18) 
      //=> 3
    const girls = db .query (p => p .gender == 'woman' && p .age < 13) 
      //=> [{gender: "woman", age: 9}]
    
    

    This is an implementation of that idea:

    // depends on input object where the properties are equal-length arrays 
    const combine = (o) => {
      const es = Object .entries (o)
      const data = es [0] [1] .map ((_, i) => Object .fromEntries (es .map (([k, v]) => [k, v [i]])))
      return {
        query: (fn) => data .filter (fn),
        count: (fn) => data .filter (fn) .length
      }
    }
    
    const db = combine ({
      gender: ['man', 'woman', 'man', 'man', 'woman'], 
      age: [30, 20, 13, 63, 9]
    })
    
    console .log ('You can count those matching a predicate, using `count`')
    console .log ('# Adults: ',   db .count (p => p .age >= 18))
    console .log ('# Boys: ',     db .count (p => p .gender == 'man' && p .age < 18))
    console .log ('# Children: ', db .count (p => p .age < 18))
    console .log ('# Females: ',  db .count (p => p .gender == 'woman'))
    console .log ('# Girls: ',    db .count (p => p .gender == 'woman' && p .age < 18))
    console .log ('# Males: ',    db .count (p => p .gender == 'man'))
    console .log ('# Men: ',      db .count (p => p .gender == 'man' && p .age >= 18))
    console .log ('# Women: ',    db .count (p => p .gender == 'woman' && p .age >= 18))
    console .log ('')
    console .log ('Or you can fetch them, using `query`')
    console .log ('Drinking age', db.query (p => p .age >= 21))
    console .log ('Children: ',   db .query (p => p .age < 18))
    .as-console-wrapper {max-height: 100% !important; top: 0}

    This is not limited to two properties. If we added eyes: ['blue', 'brown', 'brown', 'hazel', 'blue'] to our input, then the objects we’re investigating would have eyes properties as well.

    There is one important caveat: the arrays of properties supplied must have the same lengths. That is inherent in the problem statement, of course, but what it really means is that this technique is fairly limited.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search