freeCodeCamp Algorithm Challenge Guide: Inventory Update

freeCodeCamp Algorithm Challenge Guide: Inventory Update
0

#1

:triangular_flag_on_post: Remember to use Read-Search-Ask if you get stuck. Try to pair program :busts_in_silhouette: and write your own code :pencil:

:checkered_flag: Problem Explanation:

In this problem, you’ve to compare and update the inventory stored in a 2D array against a second 2D array of a fresh delivery. Update the current existing inventory item quantities (in arr1). If an item cannot be found, add the new item and quantity into the inventory array. The returned inventory array should be in alphabetical order by item.

The current as well as new inventory will be in this format: var newInv = [[2, "Item1 Name"], [3, "Item2 Name"], [67, "Item3 Name"], [7, "Item4 Name"]];.

Relevant Links

:speech_balloon: Hint: 1

You need to work through each item of the new inventory to see if it exists in the current inventory or not. Remember that the product name is stored as the second element of each sub-array: arr2[0][1] = "Bowling Ball".

try to solve the problem now

:speech_balloon: Hint: 2

If the item exists, you need to add the quantity from the new inventory. If the item doesn’t exist, you need to add the entire item.

try to solve the problem now

:speech_balloon: Hint: 3

Return the completed inventory in alphabetical order.

try to solve the problem now

Spoiler Alert!

687474703a2f2f7777772e796f75726472756d2e636f6d2f796f75726472756d2f696d616765732f323030372f31302f31302f7265645f7761726e696e675f7369676e5f322e676966.gif

Solution ahead!

:beginner: Basic Code Solution:

function updateInventory(arr1, arr2) {

    // Variable for location of product
    var index;

    // A helper method to return the index of a specified product (undefined if not found)
    var getProductIndex = function (name) {
        for (var i = 0; i < this.length; i++) {
            if (this[i][1] === name) {
                return i;
            }
        }
        return undefined;
    }

    // For each item of the new Inventory
    for (var i = 0; i < arr2.length; i++) {

        // Invoke our helper function using arr1 as this
        index = getProductIndex.call(arr1, arr2[i][1]);

        // If the item doesn't exist
        if (index === undefined) {
            // Push the entire item
            arr1.push(arr2[i]);
        } else {
            // Add the new quantity of the current item
            arr1[index][0] += arr2[i][0];
        }

    }

    // Sort alphabetically, by the product name of each item
    arr1.sort(function (a, b) {
        if (a[1] > b[1]) {
            return 1;
        }
        if (a[1] < b[1]) {
            return -1;
        }
        return 0;
    });

    return arr1;
}

// test here
// Example inventory lists
var curInv = [
    [21, "Bowling Ball"],
    [2, "Dirty Sock"],
    [1, "Hair Pin"],
    [5, "Microphone"]
];

var newInv = [
    [2, "Hair Pin"],
    [3, "Half-Eaten Apple"],
    [67, "Bowling Ball"],
    [7, "Toothpaste"]
];

updateInventory(curInv, newInv);

:rocket: Run Code

Code Explanation:

  • The variable index stores the location (index) of a product.
  • The helper function getProductIndex() returns the index of a specified product. It iterates through each element of the array that it is called on until it can find the name parameter. If the product is not found in the inventory, undefined is returned.
  • Then, each item in the new inventory (delivery) is worked through:
    • index is set to the result of invoking the helper function i.e., search the new inventory for that product name and return it’s index.
    • If the item is found, quantity of the product is added to the quantity of the same product in current inventory.
    • If the item is not found, the entire product (name and quantity) is added to the current inventory.
  • The updated inventory, arr1, is then sorted by product name (held in arr1[x][1]).
  • The final - updated as well as sorted array is then returned.

Relevant Links

:sunflower: Intermediate Code Solution:

function updateInventory(arr1, arr2) {
  // All inventory must be accounted for or you're fired!

  var index;
  var arrCurInvName = []; // Names of arr1's items
  var arrNeInvName = []; // Names of arr2's items

  // Same as using two for loops, this takes care of increasing the number of stock quantity.
  arr1.map(function(item1) {
    return arr2.map(function(item2) {
      if (item1[1] === item2[1]) {
        item1[0] = item1[0] + item2[0]; //Increase number of stock
      }
    });
  });

  // Get item's name for new Inventory
  arr2.map(function(item) {
    arrNeInvName.push(item[1]);
  });

  // Get item's name for Current Inventory
  arr1.map(function(item) {
    arrCurInvName.push(item[1]);
  });

  // Add new inventory items to current inventory.
  arrNeInvName.map(function(item) {
    if (arrCurInvName.indexOf(item) === -1) {
      index = arrNeInvName.indexOf(item);
      arr1.push(arr2[index]);
    }
  });

  // Sort the array alphabetically using the second element of the array as base.
  arr1.sort(function(currItem, nextItem) {

    //Ternary function to avoid using if else
    return currItem[1] > nextItem[1] ? 1 : -1;
  });

  return arr1;
}

// test here
// Example inventory lists
var curInv = [
    [21, "Bowling Ball"],
    [2, "Dirty Sock"],
    [1, "Hair Pin"],
    [5, "Microphone"]
];

var newInv = [
    [2, "Hair Pin"],
    [3, "Half-Eaten Apple"],
    [67, "Bowling Ball"],
    [7, "Toothpaste"]
];

updateInventory(curInv, newInv);

:rocket: Run Code

Code Explanation:

  • The variable index stores the location (index) of a product.
  • arrCurInvName has the names of arr1’s items.
  • arrNeInvName has the names of arr2’s items.
  • arr1.map(function(item1)) takes care of items already existing in inventory i.e., it increases the quantity in the inventory.
  • Next, arr2.map(function(item)) and arr1.map(function(item)) get the names of items for the new and current inventory respectively.
  • arrNeInvName.map(function(item)) handles items which don’t already exist in inventory i.e., it adds new items to the inventory.
  • The updated array arr1 is then sorted alphabetically by product name (held in arr1[x][1]) and returned.

Relevant Links

:rotating_light: Advanced Code Solution:

function updateInventory(arr1, arr2) {
  var flag = 0;
  arr2.forEach(function(item) {
    flag = 0;
    arr1.forEach(function(list) {
      // If the product is already present, increase the quantity
      if(item[1] === list[1]) {
        list[0] += item[0];
        flag = 1;
      }
    });
    //If not already present, add the product
    if(flag === 0)
      arr1.push(item);
  });

  //Return the sorted inventory in alphabetical order by product name
  return arr1.sort(function(a, b) {
    return a[1] > b[1] ? 1 : -1;
  });
}

// test here
// Example inventory lists
var curInv = [
    [21, "Bowling Ball"],
    [2, "Dirty Sock"],
    [1, "Hair Pin"],
    [5, "Microphone"]
];

var newInv = [
    [2, "Hair Pin"],
    [3, "Half-Eaten Apple"],
    [67, "Bowling Ball"],
    [7, "Toothpaste"]
];

updateInventory(curInv, newInv);

:rocket: Run Code

Code Explanation:

  • The variable flag is a flag variable which checks whether a product exists in the inventory. A flag variable, in its simplest form, is a variable you define to have one value until some condition is true, in which case you change the variable’s value.
  • arr2.forEach(function(item)) iterates through each item in the new delivery:
    • If the product is already present, it’s quantity is simply updated and flag is made 1.
    • If the product is new i.e., flag is 0, it is added to the inventory using the push() method.
  • The updated array arr1 is then sorted alphabetically by product name.

Relevant Links

:clipboard: NOTES FOR CONTRIBUTIONS:

  • :warning: DO NOT add solutions that are similar to any existing solutions. If you think it is similar but better, then try to merge (or replace) the existing similar solution.
  • Add an explanation of your solution.
  • Categorize the solution in one of the following categories — Basic, Intermediate and Advanced. :traffic_light:
  • Please add your username only if you have added any relevant main contents. (:warning: DO NOT remove any existing usernames)

See :point_right: Wiki Challenge Solution Template for reference.


#2

#3

#4

My solution:


function updateInventory(arr1, arr2) {
    arr2.forEach(function(newItem, newPos, newArr) {
      arr1.forEach(function(currentItem, currentPos, currentArr) {
        if (currentItem[1] === newItem[1]) {
          currentItem[0] += newItem[0];
          arr2.splice(newPos, 1);
        }
      });
    });
  
  return arr1.concat(arr2).sort(function(a, b) {
    if (a[1] < b[1]) {
      return -1;
    } else if (a[1] > b[1]) {
      return 1;
    } else {
      return 0;
    }
  });
}

// Example inventory lists
var curInv = [
    [21, "Bowling Ball"],
    [2, "Dirty Sock"],
    [1, "Hair Pin"],
    [5, "Microphone"]
];

var newInv = [
    [2, "Hair Pin"],
    [3, "Half-Eaten Apple"],
    [67, "Bowling Ball"],
    [7, "Toothpaste"]
];

updateInventory(curInv, newInv);

#5
function updateInventory(current, shipment) {
    //combine all inventory together
    var inventory = current.concat(shipment);
    var length = inventory.length, i = 0;
    
    //iterate through array
    while (i < length) {
      var j = i + 1;
      
      //seek and destroy duplicates; decrease length when one is destroyed
      while(j < length) {
        if(inventory[i][1] === inventory[j][1]) {
          inventory[i][0] += inventory[j][0];
          inventory.splice(j, 1);
          length--;
        }
        j++;
      }
      
      i++;
    }
    
    //sort ascending by product name
    return inventory.sort(function(a, b) {
      if(a[1] < b[1])
        return -1;
      
      if(a[1] > b[1])
        return 1;
    });
}

#6

I think my solution works pretty good and is comparable to the given solution up top. Any critiques?

Thank you!


function updateInventory(arr1, arr2) {
//iterate through each item in the second array
  for (var i = 0; i < arr2.length; i++) {
    var foundMatch = false;
//Does the current item match any existing items? If so, update their quantity    
  for (var n = 0; n < arr1.length; n ++) {
       if (arr1[n][1].indexOf(arr2[i][1]) !== -1) {
      arr1[n][0] += arr2[i][0];
//Make foundMatch true so it doesnt add the item later, outside of this iteration
           foundMatch = true;} 
       }
 //Did iterating through the array turn up a match?
   if (foundMatch === false) {
//if not, create new item
  arr1.push(arr2[i]);} 
    }
//final step, sort everything that is in the array
arr1.sort(function(a, b) {
    if (a[1] < b[1]) {
        return -1; }
    return 1;
});
return arr1;
}

// Example inventory lists
var curInv = [
    [21, "Bowling Ball"],
    [2, "Dirty Sock"],
    [1, "Hair Pin"],
    [5, "Microphone"]
];

var newInv = [
    [2, "Hair Pin"],
    [3, "Half-Eaten Apple"],
    [67, "Bowling Ball"],
    [7, "Toothpaste"]
];

updateInventory(curInv, newInv);

#7

Code golf is fun! Can anyone go shorter than this? It’s about 150 characters excluding white spaces.

function updateInventory(arr1, arr2) {
  arr2.forEach((e,i)=>{
    x=arr1.map(e=>e[1]).indexOf(e[1]);
    if(x==-1) arr1.push(e);
    else arr1[x][0]+=e[0];
  });
  return arr1.sort((a,b)=>a[1]>b[1]);
}

#8

@cambsCoder
Hi! Mine is about 142 without spaces :slight_smile:

function updateInventory(arr1, arr2) {
  return arr2.map(v=>(f=arr1.find(a=>a[1]==v[1]))?[v[0]+f[0],v[1]]:v)
  .concat(arr1.filter(a=>!arr2.find(b=>b[1]==a[1]))).sort((a,b)=>a[1]>b[1]);
}

But if you’ll write your implementation in one line it’ll be shorter than mine by three characters :wink:


#9

I think I found a much simpler way to solve this using basic code. Simply concat the two arrays, sort them by the item names, then loop through them, starting from the end of the array, adding the totals and splicing out the duplicate arrays.

function updateInventory(arr1, arr2) {
  //concatenate the argument arrays
  arr1 = arr1.concat(arr2);
  
  //sort the array by the item names
  arr1.sort(function(a, b){
    return a[1] > b[1];
  })
  
  //Set up a loop to go through each array, starting from the end.  Note you have to end when i is 1.
  for (var i = arr1.length - 1; i >= 1; i--){
    //check to see if each name matches the one ahead of it
    if (arr1[i][1] === arr1[i-1][1]){
      //if you find a match, add the numbers
      arr1[i-1][0] += arr1[i][0];
      // then splice out the duplicate
      arr1.splice(i, 1);
    }
  }
  return arr1
}

var curInv = [
    [21, "Bowling Ball"],
    [2, "Dirty Sock"],
    [1, "Hair Pin"],
    [5, "Microphone"]
];

var newInv = [
    [2, "Hair Pin"],
    [3, "Half-Eaten Apple"],
    [67, "Bowling Ball"],
    [7, "Toothpaste"]
];

updateInventory(curInv, newInv);

#10

I got to 142, but only by removing every possible character AND changing the names of the function and arguments. The function alone has 14 more characters than it really needs.

function u(x,y){x=x.concat(y).sort((a,b)=>a[1]>b[1]);for(i=x.length-2;i--;){if(x[i][1]==x[i+1][1]){x[i+1][0]+=x[i][0];x.splice(i,1)}}return x}


#11

Mine is a bit clumsy, but I did not see anyone go this way so here it is:

function updateInventory(arr1, arr2) {
  // Arrey to hold all items' names
  var itemList = [];
  
  // Turn the arr1 into a JSON
  var inv = arr1.reduce(function (acc, a) {
    acc[a[1]] = {amount:a[0]};
    itemList.push(a[1]);
    return acc;
  }, {});

  // Check each item from the arr2 against the inventory
  for (i = 0; i < arr2.length; i++) {
    
    // Add amount if item is present
    if (inv[arr2[i][1]]) {inv[arr2[i][1]].amount += arr2[i][0];}
    else {
      
      // Create new item in inv if not present
      itemList.push(arr2[i][1]);
      inv[arr2[i][1]] = {amount:arr2[i][0]};
    }
  }

  // Generate the result based on the alphabetically sorted itemList
  return itemList.sort().reduce(function(acc, item) {
    acc.push([inv[item].amount, item]);
    return acc;
  },[]);
}

#12

LOL…looks like I did

function updateInventory(arr1, arr2) {
  var A = [];
  var currInvObj = arr1.reduce(function(obj, arr) {
    obj[arr[1]] = arr[0];
    return obj;
  }, { }); 

  for (var i = 0; i < arr2.length; i++) {
    if (currInvObj.hasOwnProperty(arr2[i][1])) currInvObj[arr2[i][1]] += arr2[i][0];
    else A.push([arr2[i][0], arr2[i][1]]);
  } 
  
  for ( var prop in currInvObj) {
    A.push([currInvObj[prop], prop]);
  }
  
  A.sort((a, b) =>a[1] > b[1]);
  return A;
}

#13

My solution with some destructuring to make it more readable

function updateInventory(arr1, arr2) {
  for (let [quantity, name] of arr2) {
    const index = getItemIndex(arr1, name);
    if (index === -1) {
      arr1.push([quantity, name]);
    } else {
      arr1[index][0] += quantity;
    }
  }
  return arr1.sort(([q1, n1], [q2, n2]) => n1.localeCompare(n2));
}

#14

Do this with Objects:

function updateInventory(arr1, arr2) {
    // All inventory must be accounted for or you're fired!
    var items = {};
    for (var i = 0; i < arr1.length; i++) {
      items[arr1[i][1]] = arr1[i][0]; 
    }
    for (i = 0; i < arr2.length; i++) {
      items[arr2[i][1]] = items[arr2[i][1]] === undefined ? arr2[i][0] : items[arr2[i][1]] + arr2[i][0]; 
    }
   
  result = [];
  for (item in items) {
    result.push([items[item], item]);
  }
  

    return result.sort((a,b) => a[1] > b[1]);
}

#15

Here is my solution by converting the current inventory to an object for easy data manipulation and using Sting.prototype.localeCompare() to sort when converted back to an array:

function updateInventory(arr1, arr2) {
  //Make copies of the arrays for clarity sake
  var copy_curInv = arr1, copy_newInv = arr2,
      //convert current inventory to an object for easy data manipulation
      objCurInv = copy_curInv.reduce(function(acc, cur){
        acc[cur[1]] = cur[0];
        return acc;
      }, {}),
      //sorted
      sortedArr = [];
  
  //Loop thru the new inventory and update or create new inventory
  copy_newInv.forEach(function(inv){
    //If new inventory exists in current inventory,   
    if(objCurInv.hasOwnProperty(inv[1])){
      objCurInv[inv[1]] += inv[0]; //update it's quantity (by increment)
    }else{//OR
      objCurInv[inv[1]] = inv[0]; //create a new inventory
    }
  });
  
  //Convert the updated current inventory back to an array
  for(var currInv in objCurInv){
    sortedArr.push([objCurInv[currInv], currInv]);
  }
  
  //sort alphabetically using String.prototype.localeCompare()
  sortedArr.sort(function(a, b){
    return a[1].localeCompare(b[1]);
  });
  
  return sortedArr; //The result
}



#16

Here is my own solution, without a loop within a loop, separating the names make it possible.

function updateInventory(arr1, arr2) {
  // All inventory must be accounted for or you're fired!

  // get all our current inventory items, so we can reference it later.
  var items = arr1.map(function(item){
    return item[1];
  });

  return arr2.reduce(function(inventory,item){
      var i = items.indexOf(item[1]);
      if(i >= 0){
        inventory[i][0] += item[0];
      }
      else{
        inventory.push(item);
      }
      return inventory;
  },arr1).sort(function(a,b){ return a[1] > b[1]; });
}

#17
function updateInventory(arr1, arr2) {
	var newArr = [];

	var obj = arr1.reduce(function(acc, curr) {
		acc[curr[1]] = curr[0];
		return acc;
	}, {});

	var obj2 = arr2.reduce(function(acc, curr) {
		acc[curr[1]] = curr[0];
		return acc;
	}, {});
	
	for (var key in obj2) {
		if (key in obj) {
			obj[key] += obj2[key];
		} else {
			obj[key] = obj2[key];
		}
	}
		
	for (var k in obj) {
		newArr.push([obj[k], k]);
	}

	newArr = newArr.sort(function(a, b) {
		return a[1] > b[1];
	});
	return newArr;
}

#18

I enjoyed this one!

function updateInventory(arr1, arr2) {
  
  let currentItems = [],
      newItems;
    
  arr1.map((item, index) => {
    arr2.map((elem, ind) => {
      if(item[1] === elem[1]) {
        let curStock = parseInt(elem[0]), 
            newStock = parseInt(item[0]);
           
        return item[0] = curStock + newStock;
      }
    })
  })
  
  arr1
    .reduce((prv, cur) => currentItems
    .push(cur[1]), 0);
  
  newItems = arr2
    .filter((item, index) => !currentItems
      .includes(item[1]))
    .reduce((prv, next) => arr1
      .push(next), 0);
    
  arr1.sort((a, b) => a[1] > b[1]);
  
  return arr1
}

#19

I did this one without sorting. Instead, I inserted the new items in the correct position. I believe it’s more efficient this way since you only go through the arrays once.

function updateInventory(currentInventory, newInventory) {
        
    let updatedInventory = currentInventory;

    for (const newItem of newInventory) {
        let index = updatedInventory.length;
        let existing = false;

        for (let i = 0; i < updatedInventory.length; i++) {
            
            if (newItem[1] === updatedInventory[i][1]) {
                updatedInventory[i][0] += newItem[0];
                existing = true;
                break;
            } 
            
            if (newItem[1] < updatedInventory[i][1]) {
                index = i;
                break;
            } 
        }

        if (!existing) {
            updatedInventory = updatedInventory.slice(0, index).concat([newItem], updatedInventory.slice(index));
        } 
    }

    return updatedInventory;
}

#20

I did by extracting keys of old inventory and comparing it to the second and sorting the final array

function updateInventory(arr1, arr2) {
    // All inventory must be accounted for or you're fired!
  var keys = arr1.map( i=>i[1] );
  arr2.forEach( item=>{
    var k = keys.indexOf(item[1]);
    if(k!= -1){
      arr1[k][0] += item[0];
    }else{
      arr1.push(item);
    }
  });
  console.log(arr1);
  return arr1.sort(function(a, b) {
    return a[1] > b[1] ? 1 : -1;
  });
}

// Example inventory lists
var curInv = [
    [21, "Bowling Ball"],
    [2, "Dirty Sock"],
    [1, "Hair Pin"],
    [5, "Microphone"]
];

var newInv = [
    [2, "Hair Pin"],
    [3, "Half-Eaten Apple"],
    [67, "Bowling Ball"],
    [7, "Toothpaste"]
];

updateInventory(curInv, newInv);