2

I have an array of objcets. With reduce method after looping i want to receive two seperate arrays. Now, i am doing this way.

const dogs = [
  { weight: 22, curFood: 250, owners: ['Alice', 'Bob'] },
  { weight: 8, curFood: 200, owners: ['Matilda'] },
  { weight: 13, curFood: 275, owners: ['Sarah', 'John'] },
  { weight: 32, curFood: 340, owners: ['Michael'] },
];

So, with the below reduce methods i am receiving the result that i want (2 array).

const ownersEatTooMuch = dogs.reduce(function (acc, dog) {
  if (dog.curFood > dog.recommended * 1.1) {
    acc.push(dog.owners);
  }
  return acc.flat();
}, []);

const ownersEatTooLess = dogs.reduce(function (acc, dog) {
  if (dog.curFood < dog.recommended * 0.9) {
    acc.push(dog.owners);
  }
  return acc.flat();
}, []);

But is it possible to merge this into one reduce method to create 2 arrays. I imagine situation like this,

const [ownersEatTooMuch1, ownersEatTooLess1] = dogs.reduce(function (dog) {
  // When the condition will be true i want to fill first array ownersEatTooMuch1 and when another condition will be true i want to fill second array ownersEatTooLess1
}, [[], []]);
const [ownersEatTooMuch1, ownersEatTooLess1] = dogs.reduce(
  function (dog) {
    if (dog.curFood > dog.recommended * 1.1) {
      acc.push(dog.owners);
    }
  },
  [[], []]
);

I just don't understand how to determine [[], [] between these and then push into ownersEatTooMuch1 or here ownersEatTooLess1

7
  • 3
    Don't think reduce will help you here, why not just loop over the array and push to 2 seperate arrays? Commented Jul 3, 2024 at 13:55
  • 1
    If i ran your code with 2 seperate reduce's (Your code above), i get just empty arrays. What output do you expect? Commented Jul 3, 2024 at 13:55
  • 2
    Also your code will not work since recommended is never defined anywhere. Commented Jul 3, 2024 at 13:58
  • Not what you're after, but you could rewrite your current .reduce() as dogs.filter(dog => dog.curFood > dog.recommended * 1.1).flatMap(({ owners }) => owners) Commented Jul 3, 2024 at 14:02
  • Why do you want this? What is not good about your current code? Commented Jul 3, 2024 at 14:23

9 Answers 9

1

Sure:

const dogs = [
  { weight: 22, curFood: 250, recommended: 200, owners: ['Alice', 'Bob'] },
  { weight: 8, curFood: 200, recommended: 250, owners: ['Matilda'] },
  { weight: 13, curFood: 275, recommended: 400, owners: ['Sarah', 'John'] },
  { weight: 32, curFood: 340, recommended: 340, owners: ['Michael'] },
];
const {tooMuch, tooLess} = dogs.reduce(function (acc, dog) {
  if (dog.curFood > dog.recommended * 1.1) {
    acc.tooMuch = acc.tooMuch.concat(dog.owners);
  }
  else if (dog.curFood < dog.recommended * 0.9) {
    acc.tooLess = acc.tooLess.concat(dog.owners);
  }
  return acc;
}, {tooMuch: [], tooLess: []});

console.log(tooMuch);
console.log(tooLess);

Explanation:

  • we pass an object containing two empty arrays called tooMuch and tooLess
  • inside the function we do both comparisons
  • with else if, since we exclude someone eating too much and too less at the same time
  • if the criteria is fulfilled, then we concat the respective array with the owners at each step
  • returning acc
  • at the end, we destructure the resulting object into two constants, called tooMuch and tooLess, both being arrays
  • which then can be used further as normal constants and we console log them to illustrate this fact

I have added a recommended field to each dog so the comparisons will make sense.

Sign up to request clarification or add additional context in comments.

Comments

1

Not the reduction you were asking for, but you could use one expression involving filter and flatMap:

const dogs = [{ weight: 22, curFood: 250, recommended: 400, owners: ['Alice', 'Bob'] },
              { weight: 8,  curFood: 200, recommended: 80,  owners: ['Matilda'] },
              { weight: 13, curFood: 275, recommended: 130, owners: ['Sarah', 'John'] },
              { weight: 32, curFood: 340, recommended: 500, owners: ['Michael'] }];

const [ownersEatTooLess, ownersEatTooMuch] = [0.9, 1.1].map((factor, i) =>
    dogs.filter(dog => (dog.curFood >= dog.recommended * factor) == i)
        .flatMap(({owners}) => owners)
);

console.log({ownersEatTooMuch, ownersEatTooLess});

This is not the fastest running code, but could be perceived as "elegant" (a matter of opinion).

Note that it will deal differently when the ratio is exactly 1.1. In that case the owners will also be added in the "Too much" group. I assume this is not a problem. You can always adjust the factor to your liking.

Secondly, the sloppy == is intended here. If you prefer using ===, then convert the left operand to number with a unary + operator.

2 Comments

Thanks for this reply, before reduce method, I already tried this way it worked, but I have done it also separately, it is better to create together. My goal is to train with the reduce method. Thanks for the help
"it is better to create together": better in what way?
0

I hope this is what you are looking for. Because your example code do not work (recommended undefined), and the output is not clear, im not sure if this is what you want.

Based on the code you provided you want 2 arrays with "ate too much" or "ate too less".

What about the cases which is neither "too much" or "too less"? See my comment in the array definition: does not apppear anywhere, neither "too much" or "too less"

const dogs = [
    { weight: 22, curFood: 250, owners: ['Alice', 'Bob'], recommended: 10 },
    { weight: 8, curFood: 200, owners: ['Matilda'], recommended: 2000 },
    { weight: 13, curFood: 275, owners: ['Sarah', 'John'], recommended: 300 }, // does not apppear anywhere, neither "too much" or "too less"
    { weight: 32, curFood: 340, owners: ['Michael'], recommended: 1000 },
];



const [ownersEatTooMuch, ownersEatTooLess] = dogs.reduce((arr, dog) => {
    
    if (dog.curFood > dog.recommended * 1.1) {
        arr[0].push(...dog.owners);
    }

    if (dog.curFood < dog.recommended * 0.9) {
        arr[1].push(...dog.owners);
    }

    return arr;

}, [
    [], // too much
    []  // too less
]);


console.log(ownersEatTooLess, ownersEatTooMuch)

The reduce iteration returns every time the initial array passed to it, which is a 2 layer array.

  1. Item = Array for people "too much",
  2. Item = Array for people "too less".

These arrays get then filled based on your calculation.

The 2 layer array returned from reduce is then deconstructed into the variables ownersEatTooMuch & ownersEatTooLess.

Comments

0

it's easy. You can do something like that:

const result = dogs.reduce((acc, curr)=> {      
 if (dog.curFood > dog.recommended * 1.1) {
    acc.a.push(dog.owners);
 } else {
    acc.b.push(dog.owners);
 }
return acc
}, 
{
    a:[],
    b:[]
}) 

You should just update the result key to avoid duplication code ( i don't know your specific logic)

    const result = dogs.reduce((acc, curr)=> {      
let resultKey = 'default'

 //condition a
 if (dog.curFood > dog.recommended * 1.1) {
    resultKey = 'a'
 } else {
    resultKey = 'b'     
 } //...else if for any others keys...
     
 acc[resultKey].push(dog.owners);
return acc
}, 
{
    default: [],
    a:[],
    b:[]
}) 

Comments

0

I think what you need is the ... (spread) operator.


const dogs = [
  { weight: 22, curFood: 250, recommended: 224, owners: ['Alice', 'Bob'] },
  { weight: 8, curFood: 200,recommended: 300, owners: ['Matilda'] },
  { weight: 13, curFood: 275, recommended: 300, owners: ['Sarah', 'John'] },
  { weight: 32, curFood: 340, recommended: 300, owners: ['Michael'] },
];

const [ownersEatTooMuch, ownersEatTooLess] = dogs.reduce(function (acc, dog) {
  const [ownersEatTooMuch, ownersEatTooLess] = acc;
  if (dog.curFood > dog.recommended * 1.1) {
    ownersEatTooMuch.push(...dog.owners);
  }else if(dog.curFood < dog.recommended * 0.9){
    ownersEatTooLess.push(...dog.owners);
  }
  return acc;
}, [[], []]);


Comments

0

First of all, lets get the purpose of using the 'reduce' method here, reduce is expected to be used in situations where the array/list values supposed to go through some logic and return a single value.

In your case, we could approach this with a simple forEach() function,

const dogs = [
  { weight: 22, curFood: 250, owners: ['Alice', 'Bob'] },
  { weight: 8, curFood: 200, owners: ['Matilda'] },
  { weight: 13, curFood: 275, owners: ['Sarah', 'John'] },
  { weight: 32, curFood: 340, owners: ['Michael'] },
];

function dogsRecommendedPortion(dogs) {   
    dogs.forEach(function (d) {     
     d.recommended = Math.trunc(d.weight ** 0.75 * 28);   
    });
 }  

dogsRecommendedPortion(dogs);

let eatToomuch = []
let eatTooLess = []


dogs.forEach(function(dog){
     if (dog.curFood > dog.recommended * 1.1) {
        eatToomuch.push(dog.owners);
      } else if (dog.curFood < dog.recommended * 0.9) {
         eatTooLess.push(dog.owners);
      }
 })

console.log(eatToomuch.flat())
console.log(eatTooLess.flat())

Comments

0

The reducer of this snippet delivers an array with 3* subarrays. The reducer result is destructured into 3 variables.

I had to guess the recommended value for the demo I used a function based on the function you gave in the comment section of your question to set recommended values.

* Why 3? Because I guess there may be 'undecided' values, given the comparison you gave.

const dogs = setRecommendedPortions([
  { weight: 22, curFood: 250, owners: ['Alice', 'Bob'] },
  { weight: 8, curFood: 200, owners: ['Matilda'] },
  { weight: 13, curFood: 275, owners: ['Sarah', 'John'] },
  { weight: 32, curFood: 340, owners: ['Michael'] } ]);
const [under, over, undecided] = dogs.reduce(dogsReducer, []);

console.log([
  `under [${under}]`,
  `over  [${over}]`,
  `undecided (none of the above) [${undecided}]`]
.join(`\n`));

function dogsReducer(result, { curFood, owners, recommended }) {
  // determine index of result array to concatenate to
  // based on currFood compared to recommended value
  const index = curFood < 0.9 * recommended 
    ? 0 : curFood > 1.1 * recommended ? 1 : 2;
  // concatenate to the relevant array and return result
  // (create empty array and concat when not existing yet)
  return (result[index] = (result[index] ?? []).concat(owners), result);
}

function setRecommendedPortions(dogs) {   
  return dogs.map(dog => 
    ({...dog, recommended: Math.trunc(dog.weight**0.75 * 28)}) );
}

Comments

0

Give this a try:

const dogs = [
  { weight: 22, curFood: 250, owners: ['Alice', 'Bob'] },
  { weight: 8, curFood: 200, owners: ['Matilda'] },
  { weight: 13, curFood: 275, owners: ['Sarah', 'John'] },
  { weight: 32, curFood: 340, owners: ['Michael'] },
];

dogsRecommendedPortion(dogs);

const [eatTooMuch, eatTooLittle] = dogs.reduce(
  (acc,{curFood,recommended,owners}) =>
    [
      curFood > recommended * 1.1 ? [...acc[0],...owners] : acc[0],
      curFood < recommended * 0.9 ? [...acc[1],...owners] : acc[1]
    ],
    [[],[]]
);

console.log(eatTooMuch);
console.log(eatTooLittle);

function dogsRecommendedPortion(dog) {
  dog.forEach(function (d) {
    d.recommended = Math.trunc(d.weight ** 0.75 * 28);   
  }); 
}

Or to finish from where you left off:

const dogs = [
  { weight: 22, curFood: 250, owners: ['Alice', 'Bob'] },
  { weight: 8, curFood: 200, owners: ['Matilda'] },
  { weight: 13, curFood: 275, owners: ['Sarah', 'John'] },
  { weight: 32, curFood: 340, owners: ['Michael'] },
];

dogsRecommendedPortion(dogs);

const [eatTooMuch, eatTooLittle] = dogs.reduce(
  function(acc,dog) {
    if (dog.curFood > dog.recommended * 1.1) {
      acc[0].push(...dog.owners);
    }
    if (dog.curFood < dog.recommended * 0.9) {
      acc[1].push(...dog.owners);
    }
    return acc;
  },
  [[], []]
);

console.log(eatTooMuch);
console.log(eatTooLittle);

function dogsRecommendedPortion(dog) {
  dog.forEach(function (d) {
    d.recommended = Math.trunc(d.weight ** 0.75 * 28);   
  }); 
}

Comments

-1

Array push

Array push with spread syntax

You can use push with the spread syntax to add elements to each array:

const dogs = [
  { weight: 22, curFood: 250, owners: ['Alice', 'Bob'], recommended: 200 },
  { weight: 8, curFood: 200, owners: ['Matilda'], recommended: 150 },
  { weight: 13, curFood: 275, owners: ['Sarah', 'John'], recommended: 350 },
  { weight: 32, curFood: 340, owners: ['Michael'], recommended: 400 },
];

const [ownersEatTooMuch, ownersEatTooLess] = dogs.reduce(([a, b], dog) => {
    if (dog.curFood > dog.recommended * 1.1) {
    a.push(...dog.owners);
  } else if (dog.curFood < dog.recommended * 0.9) {
    b.push(...dog.owners);
  }
  return [a, b];
}, [[], []]);

Array push with Iteration

You can use push and then iterate through the array of owners instead of using flat:

const dogs = [
  { weight: 22, curFood: 250, owners: ['Alice', 'Bob'], recommended: 200 },
  { weight: 8, curFood: 200, owners: ['Matilda'], recommended: 150 },
  { weight: 13, curFood: 275, owners: ['Sarah', 'John'], recommended: 350 },
  { weight: 32, curFood: 340, owners: ['Michael'], recommended: 400 },
];

const [ownersEatTooMuch, ownersEatTooLess] = dogs.reduce(([a, b], dog) => {
    if (dog.curFood > dog.recommended * 1.1) {
    for (var owner of dog.owners) {
        a.push(owner);
    }
  } else if (dog.curFood < dog.recommended * 0.9) {
    for (var owner of dog.owners) {
        b.push(owner);
    }
  }
  return [a, b];
}, [[], []]);

Array concat

You can use reduce with the Array concat method to create two arrays, each iteration may create a new array since concat is not an in-place destructive operation:

const dogs = [
  { weight: 22, curFood: 250, owners: ['Alice', 'Bob'], recommended: 200 },
  { weight: 8, curFood: 200, owners: ['Matilda'], recommended: 150 },
  { weight: 13, curFood: 275, owners: ['Sarah', 'John'], recommended: 350 },
  { weight: 32, curFood: 340, owners: ['Michael'], recommended: 400 },
];

const [ownersEatTooMuch, ownersEatTooLess] = dogs.reduce(([a, b], dog) => {
    if (dog.curFood > dog.recommended * 1.1) {
    return [a.concat(dog.owners), b];
  } else if (dog.curFood < dog.recommended * 0.9) {
    return [a, b.concat(dog.owners)];
  }
  return [a, b];
}, [[], []]);

Performance Benchmark

I ran a performance benchmark in the browser using JSBenchmark: https://jsbm.dev/u71R5v8BlIElW

JSbenchmark of different approaches

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.