frosty
frosty

Reputation: 15

how to use reduce function to simplify this code

I've an array: var old_array = [1, 2, 3, 4, 5]

If 5 exists in this array, remove it otherwise add it.

var new_array = [];
var number_existed = false;
for(var i=0,len=old_array.length;i<len;i++) {
   if(old_array[i] == 5) {
   new_array = old_array.slice(i+1);
   number_existed = true;
   break;
   } else {
      new_array.push( old_array[i] );
   }
}

if(!number_existed) {
    new_array.push(5);
}

It works, but I need improvements. How can I improve this code. I wanna see reduce function in action but just not able to come up with anything logical.

Upvotes: 1

Views: 70

Answers (7)

Mattias Martens
Mattias Martens

Reputation: 1667

If you really want to use reduce():

  function togglePresenceOfFive(array) {
  if (array.length === 0) { // Reduce can't handle this case because the loop never runs
        return [5];
    } else {
        const reduceResult = array.reduce((acc, next, index, arr) => {
            const isLast = index === arr.length - 1;

            if (next === 5) {
                return {
                    newArray: acc.newArray,
                    fiveFound: true
                };
            } else if (!acc.fiveFound && isLast) {
                return {
                    newArray: acc.newArray.concat(next, 5),
                    fiveFound: false
                }
            } else {
                return {
                    newArray: acc.newArray.concat(next),
                    fiveFound: acc.fiveFound
                };
            }
            },
            { newArray: [], fiveFound: false }
        );

        return reduceResult.newArray; 
    }
}

  return reduceResult.newArray;
}

But this is more readable in my opinion (assuming you don't mind mutating the original array):

function togglePresenceOfFive(array) {
    const indexOfFive = array.indexOf(5);

    if (indexOfFive > -1) {
        array.splice(indexOfFive, 1);
    } else {
        array.push(5);
    }

    return array;
}

Upvotes: 0

Scott Sauyet
Scott Sauyet

Reputation: 50807

One more solution, expanding on my comment:

Jeto's answer should solve this. There is no real reason to use reduce for this, as it would just complicate things. If you were just filtering out the existing 5s, reduce would be an overcomplicated but not unreasonable alternative to filter. But since you also want to add a 5 if it's not there, your accumulator is going to need to carry extra state -- or you would store it somewhere worse. Such code is feasible, but quite ugly compared to the alternatives.

Here is what such uglier code might look like:

const addOrRemove5 = (arr) => { 
  const {found, xs} = arr.reduce(
    ({found, xs}, x) => x === 5 ? {found: true, xs} : {found, xs: [...xs, x]},
    {found: false, xs: []}
  )
  return found ? xs : [...xs, 5]
}

console.log(addOrRemove5([1, 5, 2, 3, 4, 5])) //=> [1, 2, 3, 4]
console.log(addOrRemove5([1, 2, 3, 4]))       //=> [1, 2, 3, 4, 5]

This uses an accumulator that looks like {found: boolean, xs: [number]}, starting with {found: false, xs: []}, updating found to be true on each 5, and adding each non-5 to the values. Then after that runs, we return either the existing values or the existing values and an additional 5, depending on the value of found.

Again, this is much uglier than Jeto's solution. I would not recommend it here. But such techniques are not terribly uncommon when you need carry additional information as well as reducing a value. And a variant of this could let you work only with expressions and not any statements, by putting the resulting {found, xs} object in a default parameter:

const addOrRemove5 = (
  arr, 
  {found, xs} = arr.reduce(
    ({found, xs}, x) => x === 5 ? {found: true, xs} : {found, xs: [...xs, x]},
    {found: false, xs: []}
  )
) => found ? xs : [...xs, 5]

Again this is not recommended in this case, but it's a useful trick to know.

Upvotes: 1

Romildo da Silva
Romildo da Silva

Reputation: 291

Using reduce is not best approach, because reduce is used to return a single item. In my solution I use findIndex and apply your logic.

function usingFunctions(old) {
	var index = old.findIndex( function(it) { return it == 5  } );
	var new_arr = old.slice( index + 1 );
	if( index < 0 ) new_arr.push(5);
	return new_arr
}

var old = [1, 2, 6, 8, 5, 3, 6, 9];
console.log( usingFunctions(old) );
old = [1, 2, 6, 8, 3, 6, 9];
console.log( usingFunctions(old) );

Upvotes: 1

Get Off My Lawn
Get Off My Lawn

Reputation: 36351

You can find it using indexOf to get the index, and remove it using splice or add it using push

var array1 = [1, 2, 3, 4, 5]

function modify_array(value, array) {
  let idx = array.indexOf(value)
  idx > -1 ? array.splice(idx, 1) : array.push(value)
  return array
}

// Test cases

// Remove 5
console.log(modify_array(5, array1))
// Add 10
console.log(modify_array(10, array1))

Upvotes: 0

Jeto
Jeto

Reputation: 14927

You can check if the element already exists with Array.includes, then either Array.filter it to remove it, or append it using spread syntax:

function addOrRemove5(arr) {
  return arr.includes(5) ? arr.filter(v => v !== 5) : [...arr, 5];
}

console.log(addOrRemove5([1, 2, 3, 4, 5]));
console.log(addOrRemove5([1, 2, 3, 4]));

Upvotes: 3

Jonas Wilms
Jonas Wilms

Reputation: 138567

 let fiveFound = false;
 const result = input.filter(it => it !== 5 || !(fiveFound = true));
 if(!fiveFound) result.push(5);

reduce won't help you here.

Upvotes: 1

Maheer Ali
Maheer Ali

Reputation: 36594

You don't need reduce() just use filter()

var old_array = [1, 2, 3, 4, 5]

var new_array = old_array.filter(x => x === 5);
console.log(new_array);

Upvotes: 0

Related Questions