Getting previous objects value in current object - javascript

I am using a dataset that contains around 65k data. I am mapping over the dataset multiple times to massage the dataset. After obtaining the dataset in the required format, I am using map to do some computations with the price of the current item. But, whenever I return the current object, it contains the computation data of the previous object.
Whenever I log the data, it always shows the current object and the computations based on the current object. But, the returned object contains a previous object's data. Here is the route:
const {priceBands} = require('../utils/profitComputations');
let profitArray = [];
//calculating the price bands
profitArray = _.map(nfoArray, item=>{
console.log(item.cmp);
//returns the current market price; getting the correct value here
let priceBandVar = priceBands(Number(item.cmp));
console.log(priceBandVar);
//applying some algorithms; getting the correct value here
return {item: item.cmp, profitBand: priceBandVar};
//Here I find a mismatch between the parameter and the calculations
});
Here is the priceBands function in my 'utils/profitComputations':
const _ = require('lodash');
const priceBandInterval = {'-4':0, '-3':0, '-2':0, '-1':0, '0floor':0,'0ceil':0,'1':0, '2':0, '3':0, '4':0};
let priceBands = {};
module.exports = {
priceBands: function(price){
let factor = 0;
if(price>=10000){
factor = 100;
}else if (price>=1000 && price<10000){
factor = 50;
}else if (price>=500 && price<1000){
factor = 25;
}else if (price>=100 && price<500){
factor = 10;
}else if(price>=25 && price<100){
factor = 2;
}else{
factor = 0.5;
}
let priceCeil, priceFloor;
if((price%factor) == 0){
priceCeil = price + factor;
priceFloor = price - factor;
} else {
const remainder = price%factor;
priceCeil = price - remainder + factor;
priceFloor = price - remainder;
}
_.map(Object.keys(priceBandInterval), item=>{
if(parseInt(item)>0){
priceBands[item] = (parseInt(item)*factor) + priceCeil;
} else if (parseInt(item)<0){
priceBands[item] = (parseInt(item)*factor) + priceFloor;
} else {
priceBands['0floor'] = priceFloor;
priceBands['0ceil'] = priceCeil;
}
});
return priceBands;
}
}
I would appreciate if someone can share some valuable insights on what I am missing.

You must clone the variable priceBandVar because javaScript variables are called by reference. The following code is your answer:
profitArray = _.map(nfoArray, item => {
console.log(item.cmp);
//returns the current market price; getting the correct value here
let priceBandVar = priceBands(Number(item.cmp));
console.log(priceBandVar);
//applying some algorithms; getting the correct value here
return {
item: item.cmp,
profitBand: clone(priceBandVar)
};
//Here I find a mismatch between the parameter and the calculations
});
function clone(o) {
var ret = {};
Object.keys(o).forEach(function(val) {
ret[val] = o[val];
});
return ret;
}

Related

Compare an array with itself for duplicate and store in new array

I have a question, how do I compare values ​​in an array with themselves without the value to be compared being shown as true?
As an example, if the value EUW1_6011808396 occurs again, it should be stored in the array matchingMatches.
The Values of the Array are:
[EUW1_6011808396, EUW1_6011824351, EUW1_6011720277, EUW1_6010413995, EUW1_6010218048, EUW1_6010184913, EUW1_6010131700, EUW1_6009739853, EUW1_6008825456, EUW1_6008833322, EUW1_6008409245, EUW1_6008369887, EUW1_6008355242, EUW1_6007567238, EUW1_6007269146, EUW1_6007226284, EUW1_6007192332, EUW1_6005571988, EUW1_6005438941, EUW1_6005495312, EUW1_6013263286, EUW1_6013193252, EUW1_6012475324, EUW1_6012411610, EUW1_6012315128, EUW1_6012011561, EUW1_6011110477, EUW1_6011026046, EUW1_6009739853, EUW1_6006439870, EUW1_6006434580, EUW1_6005238786, EUW1_6005191249, EUW1_6005026992, EUW1_6005015187, EUW1_6004958241, EUW1_6003811368, EUW1_6002847479, EUW1_6002164371, EUW1_6002148723, EUW1_6015524685, EUW1_6015387328, EUW1_6015402003, EUW1_6014779337, EUW1_6014724668, EUW1_6014701498, EUW1_6014655368, EUW1_6014580839, EUW1_6014429620, EUW1_6014475971, EUW1_6014473252, EUW1_6013334881, EUW1_6013322375, EUW1_6012669749, EUW1_6012635347, EUW1_6012583396, EUW1_6010971941, EUW1_6006896961, EUW1_6006881165, EUW1_6006518887, EUW1_6015745842, EUW1_6015589872, EUW1_6014068520, EUW1_6014044304, EUW1_6007955310, EUW1_6003705297, EUW1_6003569783, EUW1_6002003834, EUW1_6000787500, EUW1_5994465297, EUW1_5993391050, EUW1_5992233473, EUW1_5992169601, EUW1_5984062877, EUW1_5984034743, EUW1_5983855739, EUW1_5983880569, EUW1_5983766086, EUW1_5982931745, EUW1_5982372929, EUW1_6005238786, EUW1_6005191249, EUW1_6005026992, EUW1_6005015187, EUW1_6004958241, EUW1_6002164371, EUW1_6002148723, EUW1_6002057259, EUW1_6002053660, EUW1_6002009239, EUW1_6002003834, EUW1_6001939719, EUW1_6001867883, EUW1_6001022392, EUW1_6000887143, EUW1_6000892356, EUW1_6000787500, EUW1_6000820954, EUW1_5996485374, EUW1_5994559073, EUW1_6010083174, EUW1_6010017420, EUW1_6006898776, EUW1_6006838293, EUW1_6005225782, EUW1_6005135031, EUW1_6003899867, EUW1_6003883079, EUW1_6003786523, EUW1_6002164371, EUW1_6002148723, EUW1_6002057259, EUW1_6002053660, EUW1_6002009239, EUW1_6001022392, EUW1_6000887143, EUW1_6000892356, EUW1_6000787500, EUW1_5999368247, EUW1_5999295110, EUW1_5989231240, EUW1_5989055249, EUW1_5987149834, EUW1_5978125118, EUW1_5969701977, EUW1_5969187233, EUW1_5956294382, EUW1_5955846040, EUW1_5949708234, EUW1_5934525960, EUW1_5916275391, EUW1_5916168691, EUW1_5916132470, EUW1_5907690529, EUW1_5897979620, EUW1_5897921186, EUW1_5896786548, EUW1_5880625543, EUW1_5880541891, EUW1_5878457213, EUW1_6015589872, EUW1_5969852994, EUW1_5969788713, EUW1_5940681289, EUW1_5940556247, EUW1_5937866203, EUW1_5937892773, EUW1_5937768916, EUW1_5937765393, EUW1_5934600651, EUW1_5934512860, EUW1_5934339533, EUW1_5932238516, EUW1_5932166008, EUW1_5932211862, EUW1_5929934534, EUW1_5930153889, EUW1_5931242804, EUW1_5919621815, EUW1_5918236611]
The function which is used for getting the Id for the API call.
/**
* Getting the Team Members and their IDs
*
* #param {info_id|String}
* #return Team Members IDs
* #customfunction
*/
function getTeamMembersIDs(info_id) {
try {
startup();
var teamMembers = getTeamMembersSheet();
var riotIds = [];
for (var counter = 0; counter != teamMembers.length; counter = counter + 1) {
switch (info_id){
case "puuid":
var data = buildURL("https://euw1.api.riotgames.com/lol/summoner/v4/summoners/by-name/"+teamMembers[counter]+"?api_key="+apiKey);
var puuid = data["puuid"];
riotIds.push(puuid);
break;
case "id":
var data = buildURL("https://euw1.api.riotgames.com/lol/summoner/v4/summoners/by-name/"+teamMembers[counter]+"?api_key="+apiKey);
var id = data["id"];
riotIDs.push(id);
break;
case "accountId":
var data = buildURL("https://euw1.api.riotgames.com/lol/summoner/v4/summoners/by-name/"+teamMembers[counter]+"?api_key="+apiKey);
var accountId = data["accountId"];
riotIds.push(accountId);
break;
}
}
return riotIds
} catch (err) {
Logger.log('Failed with error %s', err.message);
}
}
This Function is Used for Getting the Matches that got played per Team Member.
/**
* Getting the Data from the Api which matches got played
*
* #return the matches played
* #customfunction
*/
function getMatchesPlayed() {
try{
startup();
var puuids = getTeamMembersIDs("puuid");
var games = [];
for (var counter = 0; counter != puuids.length; counter = counter + 1) {
var data = buildURL("https://europe.api.riotgames.com/lol/match/v5/matches/by-puuid/"+puuids[counter]+"/ids?start=0&count=20&api_key="+apiKey)
for(var counter2 = 0; counter2 != data.length; counter2 = counter2 + 1){
games.push(data[counter2])
}
}
return games
}catch (err) {
Logger.log('Failed with error %s', err.message);
}
}
The function which should return the Matching Matches in the Array gets from the getMatchesPlayed function.
/**
* Checking the games for matches
*
* #return the matches which match
* #customfunction
*/
function getMatchingMatches() {
try{
var games = getMatchesPlayed();
var matchingMatches = [];
Logger.log(games)
const gettingGames = games.map(element => element)
for (var counter = 0; counter != games.length; counter = counter + 1){
Logger.log(gettingGames[counter])
}
}catch (err) {
Logger.log('Failed with error %s', err.message);
}
}
As I understand, you need to get duplicate ids.
Use the Set data structure for comparing the array values.
// assume this should return an array [EUW1_6011808396, EUW1_6011824351]
var games = getMatchesPlayed(); //
var matched_games = []
// Initialise set data structure.
var set = = new Set();
//Iterate over the games. Runtime complexity O(n)
games.forEach((element) => {
//the case when set does not contain the id we should add it to `set`
if(!set.has(element)){ // O(1)
set.add(element); // O(1)
continue;
}
// if the set already contains the id, we should add it to the matching games array.
matched_games.push(element);
})
I hope this will help.
welcome to Stackoverflow! You have a lot of ways to store duplicate values In a new array, and this is one.
// get duplicartes and store them in a array
function getDuplicates(array) {
let sorted = array.sort();
for (let i = 0; i < sorted.length - 1; i++) {
if (sorted[i + 1] == sorted[i]) {
duplicates.push(sorted[i]);
}
}
}
Another way to get non-unique values
const euws = ['EUW1_6011808396','EUW1_6011824351','EUW1_6011720277','EUW1_6010413995','EUW1_6010218048','EUW1_6010184913','EUW1_6010131700','EUW1_6009739853','EUW1_6008825456','EUW1_6008833322','EUW1_6008409245','EUW1_6008369887','EUW1_6008355242','EUW1_6007567238','EUW1_6007269146','EUW1_6007226284','EUW1_6007192332','EUW1_6005571988','EUW1_6005438941','EUW1_6005495312','EUW1_6013263286','EUW1_6013193252','EUW1_6012475324','EUW1_6012411610','EUW1_6012315128','EUW1_6012011561','EUW1_6011110477','EUW1_6011026046','EUW1_6009739853','EUW1_6006439870','EUW1_6006434580','EUW1_6005238786','EUW1_6005191249','EUW1_6005026992','EUW1_6005015187','EUW1_6004958241','EUW1_6003811368','EUW1_6002847479','EUW1_6002164371','EUW1_6002148723','EUW1_6015524685','EUW1_6015387328','EUW1_6015402003','EUW1_6014779337','EUW1_6014724668','EUW1_6014701498','EUW1_6014655368','EUW1_6014580839','EUW1_6014429620','EUW1_6014475971','EUW1_6014473252','EUW1_6013334881','EUW1_6013322375','EUW1_6012669749','EUW1_6012635347','EUW1_6012583396','EUW1_6010971941','EUW1_6006896961','EUW1_6006881165','EUW1_6006518887','EUW1_6015745842','EUW1_6015589872','EUW1_6014068520','EUW1_6014044304','EUW1_6007955310','EUW1_6003705297','EUW1_6003569783','EUW1_6002003834','EUW1_6000787500','EUW1_5994465297','EUW1_5993391050','EUW1_5992233473','EUW1_5992169601','EUW1_5984062877','EUW1_5984034743','EUW1_5983855739','EUW1_5983880569','EUW1_5983766086','EUW1_5982931745','EUW1_5982372929','EUW1_6005238786','EUW1_6005191249','EUW1_6005026992','EUW1_6005015187','EUW1_6004958241','EUW1_6002164371','EUW1_6002148723','EUW1_6002057259','EUW1_6002053660','EUW1_6002009239','EUW1_6002003834','EUW1_6001939719','EUW1_6001867883','EUW1_6001022392','EUW1_6000887143','EUW1_6000892356','EUW1_6000787500','EUW1_6000820954','EUW1_5996485374','EUW1_5994559073','EUW1_6010083174','EUW1_6010017420','EUW1_6006898776','EUW1_6006838293','EUW1_6005225782','EUW1_6005135031','EUW1_6003899867','EUW1_6003883079','EUW1_6003786523','EUW1_6002164371','EUW1_6002148723','EUW1_6002057259','EUW1_6002053660','EUW1_6002009239','EUW1_6001022392','EUW1_6000887143','EUW1_6000892356','EUW1_6000787500','EUW1_5999368247','EUW1_5999295110','EUW1_5989231240','EUW1_5989055249','EUW1_5987149834','EUW1_5978125118','EUW1_5969701977','EUW1_5969187233','EUW1_5956294382','EUW1_5955846040','EUW1_5949708234','EUW1_5934525960','EUW1_5916275391','EUW1_5916168691','EUW1_5916132470','EUW1_5907690529','EUW1_5897979620','EUW1_5897921186','EUW1_5896786548','EUW1_5880625543','EUW1_5880541891','EUW1_5878457213','EUW1_6015589872','EUW1_5969852994','EUW1_5969788713','EUW1_5940681289','EUW1_5940556247','EUW1_5937866203','EUW1_5937892773','EUW1_5937768916','EUW1_5937765393','EUW1_5934600651','EUW1_5934512860','EUW1_5934339533','EUW1_5932238516','EUW1_5932166008','EUW1_5932211862','EUW1_5929934534','EUW1_5930153889','EUW1_5931242804','EUW1_5919621815','EUW1_5918236611'];
const nonUnique = euws.reduce((r, v, i, a) => {
if (!r.includes(v) && a.indexOf(v) !== i) r.push(v);
return r;
}, []);
console.log(nonUnique)
.as-console-wrapper { max-height: 100% !important; top: 0 }

push the result of a function into an empty array

I have an array of bills and an empty array for tips.
I'm trying to call a tipcalculator function within a for loop so I can calculate the tips of each one of the bills in the bills array, store that result in the empty tips array. Is this possible to be done?
Thanks
var bills = [123,145,12,44];
var tips = [];
function calculateTips(bill){
let tip;
if(bill<10){
tip = .2;
}
if(bill>=10 && bill <20){
tip = .10;
} else {
tip = 0.1;
}
return tip * bill;
for(var i=0; i<bills.length; i++){
var temp = calculateTips(bills[i]);
tips.push(temp);
}
};
Your loop needs to be outside of the function. As the documentation says:
The return statement ends function execution and specifies a value to
be returned to the function caller.
var bills = [123,145,12,44];
var tips = [];
function calculateTips(bill){
let tip;
// Since the bill will only fall into one of your tests
// use else if, rather than an if followed by another if
if(bill < 10){
tip = .2;
} else if(bill >= 10 && bill < 20){
tip = .10;
} else {
tip = 0.1;
}
// once a function reaches a return statement
// it will return the specified value (if any)
// and then stop processing the function.
return tip * bill;
}
for(var i=0; i<bills.length; i++){
var temp = calculateTips(bills[i]);
tips.push(temp);
}
console.log(tips);
There is no difference between tip=.10 and tip=0.1. And you can shorten your script considerably, see below.
const calculateTips=bill=>((bill>=10?.1:.2)*bill).toFixed(2);
var bills = [123,145,12,44,8];
let tips=bills.map(calculateTips);
console.log(tips);
Fun fact: Your formula leads to higher tips for 8$ than for 12$ bills.

Undefined function with javascripts

I'm writing a small 'quote calculator'. I want to print the summary to the browser with innerHTML, but I'm still getting "undefined" instead of the price. But in my console.log everything works fine, I can console.log the variable price and get the expected result.
//Variables
const form = document.getElementById('request-quote');
const html = new HTMLUI();
//Event Listeners
eventListeners();
function eventListeners() {
document.addEventListener('DOMContentLoaded', function() {
//Create the <option> for the years
html.displayYears();
});
//when the form is submitted
form.addEventListener('submit', function(e) {
e.preventDefault();
//get values from the form
const make = document.getElementById('make').value;
const year = document.getElementById('year').value;
//Read the radio buttons
const level =
document.querySelector('input[name="level"]:checked').value;
// Validation
if (make === '' || year === '' || level === '') {
html.displayErrors('All fields are mendatory')
} else {
//Make the quotation
const insurance = new Insurance(make, year, level);
const price = insurance.calculateQuotation(insurance);
//print
html.showResults(price);
}
});
}
// Create another prototype with for the object HTMLUI. to print Errors
//Object
// Everything related to the calculation and quotation
function Insurance(make, year, level) {
this.make = make;
this.year = year;
this.level = level;
}
//calculation
Insurance.prototype.calculateQuotation = function(insurance) {
let price;
const base = 2000;
//get make
const make = insurance.make;
/**
1. america: 15
2. Asia : 5
3. europia: 35
*/
switch (make) {
case '1':
price = base * 1.15;
break
case '2':
price = base * 1.05;
break
case '3':
price = base * 1.35;
break
}
//get level
const level = insurance.level;
price = this.calculateLevel(price, level);
//get year
const year = insurance.year;
const difference = this.getYearDifference(year);
price = price - ((difference * 3) * price) / 100;
console.log(price);
}
//return difference between years
Insurance.prototype.getYearDifference = function(year) {
return new Date().getFullYear() - year;
// each year the cost must be 3% cheaper
}
//add value based on level
Insurance.prototype.calculateLevel = function(price, level) {
//basic increase 30%
//complete increases 50%
if (level === 'basic') {
price = price * 1.30;
} else {
price = price * 1.50;
}
return price;
}
function HTMLUI() {}
//display the latest 20 years in the select
HTMLUI.prototype.displayYears = function() {
//Max & minimum years
const max = new Date().getFullYear();
min = max - 20;
//Generate the list
const selectYears = document.getElementById('year');
//print the values
for (let i = max; i >= min; i--) {
const option = document.createElement('option')
option.value = i;
option.textContent = i;
selectYears.appendChild(option);
}
}
//Print Error, by creating a prototype
HTMLUI.prototype.displayErrors = function(message) {
//create div
const div = document.createElement('div');
div.classList = 'error';
//insert message
div.innerText = `
<p>${message}</p>
`;
form.insertBefore(div, document.querySelector('.form-group'));
//Remove the error
setTimeout(function() {
document.querySelector('.error').remove();
}, 3000);
}
HTMLUI.prototype.showResults = function(price) {
//print result
const result = document.getElementById('result');
//create a div with the result
const div = document.createElement('div');
//insert the result
div.innerHTML = `
<p class="total">Total: $ ${price}</p>
`;
//insert into html
result.appendChild(div)
}
I am expecting to print the value of the variable price( which will actually be the price) but I'm getting "undefined" while trying to print the price
You need to add in a return statement to the calculateQuotation function. Here's why, in case it helps.
The reason you're getting undefined is because the price variable is never being given a value.
On this line:
const price = insurance.calculateQuotation(insurance);
price is getting whatever the calculateQuotation function sends back with a return statement. However, in that function you're just filling a price variable which exists only in the context of that function. To make this work, I believe you need to add in a return statement at the end of the calculateQuotation function like so:
Insurance.prototype.calculateQuotation = function(insurance) {
let price;
const base = 2000;
//get make
const make = insurance.make;
/**
1. america: 15
2. Asia : 5
3. europia: 35
*/
switch (make) {
case '1':
price = base * 1.15;
break
case '2':
price = base * 1.05;
break
case '3':
price = base * 1.35;
break
}
//get level
const level = insurance.level;
price = this.calculateLevel(price, level);
//get year
const year = insurance.year;
const difference = this.getYearDifference(year);
price = price - ((difference * 3) * price) / 100;
console.log(price);
return price; // Added this in
}
It shows undefined cause you never returned a result. You need to return the 'price' variable to fix it.
...
const difference = this.getYearDifference(year);
price = price - ((difference * 3) * price) / 100;
console.log(price);
return price;
}

Iterating through object array and changing values of multiple

I have a function where I am sending in an object, wholesaler. There is an array of objects, wholesalers which contains multiple wholesaler objects.
I need the order number of the wholesaler that just came in to be increased by 1 and then find the wholesaler object that has a current ORDER of the new value and subtract 1.
$scope.wholesalers = [];
$scope.downWholesaler = function (wholesaler) {
if (wholesaler.id > 0) {
var orderNumber = wholesaler.ORDER;
var orderBelowNumber = orderNumber + 1;
angular.forEach($scope.wholesalers, function (myWholesaler) {
if (myWholesaler.ORDER === orderNumber) {
// raise current order +1
var add = orderNumber + 1
myWholesaler.ORDER = add;
}
//setting it
if (myWholesaler.ORDER === orderBelowNumber) {
//lower one with order below number -1
var subtract = orderNumber - 1;
myWholesaler.ORDER = subtract;
}
});
console.log($scope.wholesalers);
}
};
Above I am working on doing this. I go through $scope.wholesalers and increase the current objects ORDER number and then reduce the one next to it (ORDER +1) down one.
But the ORDER is going down and the next object is not changing at all. What am I doing wrong and how do I fix it?
edit: didn't have lastest code
Fix:
$scope.downWholesaler = function (wholesaler) {
if (wholesaler.id > 0) {
var add = parseInt(wholesaler.ORDER) + 1
angular.forEach($scope.wholesalers, function (myWholesaler) {
if (myWholesaler.ORDER == add) {
myWholesaler.ORDER = parseInt(myWholesaler.ORDER) - 1;
}
});
wholesaler.ORDER = add;
console.log($scope.wholesalers);
}
};

How to pick a random property from an object without repeating after multiple calls?

I'm trying to pick a random film from an object containing film objects. I need to be able to call the function repeatedly getting distinct results until every film has been used.
I have this function, but it doesn't work because the outer function returns with nothing even if the inner function calls itself because the result is not unique.
var watchedFilms = [];
$scope.watchedFilms = watchedFilms;
var getRandomFilm = function(movies) {
var moviesLength = Object.keys(movies).length;
function doPick() {
var pick = pickRandomProperty(movies);
var distinct = true;
for (var i = 0;i < watchedFilms.length; i += 1) {
if (watchedFilms[i]===pick.title) {
distinct = false;
if (watchedFilms.length === moviesLength) {
watchedFilms = [];
}
}
}
if (distinct === true) {
watchedFilms.push(pick.title);
return pick;
}
if (distinct === false) {
console.log(pick.title+' has already been picked');
doPick();
}
};
return doPick();
}
T.J. Crowder already gave a great answer, however I wanted to show an alternative way of solving the problem using OO.
You could create an object that wraps over an array and makes sure that a random unused item is returned everytime. The version I created is cyclic, which means that it infinitely loops over the collection, but if you want to stop the cycle, you can just track how many movies were chosen and stop once you reached the total number of movies.
function CyclicRandomIterator(list) {
this.list = list;
this.usedIndexes = {};
this.displayedCount = 0;
}
CyclicRandomIterator.prototype.next = function () {
var len = this.list.length,
usedIndexes = this.usedIndexes,
lastBatchIndex = this.lastBatchIndex,
denyLastBatchIndex = this.displayedCount !== len - 1,
index;
if (this.displayedCount === len) {
lastBatchIndex = this.lastBatchIndex = this.lastIndex;
usedIndexes = this.usedIndexes = {};
this.displayedCount = 0;
}
do index = Math.floor(Math.random() * len);
while (usedIndexes[index] || (lastBatchIndex === index && denyLastBatchIndex));
this.displayedCount++;
usedIndexes[this.lastIndex = index] = true;
return this.list[index];
};
Then you can simply do something like:
var randomMovies = new CyclicRandomIterator(Object.keys(movies));
var randomMovie = movies[randomMovies.next()];
Note that the advantage of my implementation if you are cycling through items is that the same item will never be returned twice in a row, even at the beginning of a new cycle.
Update: You've said you can modify the film objects, so that simplifies things:
var getRandomFilm = function(movies) {
var keys = Object.keys(movies);
var keyCount = keys.length;
var candidate;
var counter = keyCount * 2;
// Try a random pick
while (--counter) {
candidate = movies[keys[Math.floor(Math.random() * keyCount)]];
if (!candidate.watched) {
candidate.watched = true;
return candidate;
}
}
// We've done two full count loops and not found one, find the
// *first* one we haven't watched, or of course return null if
// they've all been watched
for (counter = 0; counter < keyCount; ++counter) {
candidate = movies[keys[counter]];
if (!candidate.watched) {
candidate.watched = true;
return candidate;
}
}
return null;
}
This has the advantage that it doesn't matter if you call it with the same movies object or not.
Note the safety valve. Basically, as the number of watched films approaches the total number of films, our odds of picking a candidate at random get smaller. So if we've failed to do that after looping for twice as many iterations as there are films, we give up and just pick the first, if any.
Original (which doesn't modify film objects)
If you can't modify the film objects, you do still need the watchedFilms array, but it's fairly simple:
var watchedFilms = [];
$scope.watchedFilms = watchedFilms;
var getRandomFilm = function(movies) {
var keys = Object.keys(movies);
var keyCount = keys.length;
var candidate;
if (watchedFilms.length >= keyCount) {
return null;
}
while (true) {
candidate = movies[keys[Math.floor(Math.random() * keyCount)]];
if (watchedFilms.indexOf(candidate) === -1) {
watchedFilms.push(candidate);
return candidate;
}
}
}
Note that like your code, this assumes getRandomFilm is called with the same movies object each time.

Categories