I have the following code:
var gameController = {
scores: [20, 34, 55, 46, 77],
avgScore: 112,
players: [
{name: "Ruth", playerId: 100, age: 22},
{name: "Shawnee", playerId: 101, age: 21}
]
};
var appController = {
scores: [900, 845, 809, 950],
avgScore: null,
avg: function () {
var sumOfScores = this.scores.reduce(function (prev, cur, index, array) {
return prev + cur;
});
this.avgScore = sumOfScores / this.scores.length;
}
};
gameController.avgScore = appController.avg();
console.log(gameController.avgScore);
I tried to borrow the avg method defined in appController to do the calculation for gameController. I understand that after gameController.avgScore = appController.avg();, this keyword in avg method will still point to appController as avg method was invoked by appController so I expect avgScore in gameController should remain intact, but the output is undefined, why?
avg doesn't return anything, so it implicitly returns undefined. You are basically doing gameController.avgScore = undefined;.
If you want to apply the avg method to gameController, you could use .call:
appController.avg.call(gameController);
Better would probably be to have avg as a standalone function that accepts an array of numbers as input (argument) and returns the average.
Updated:
When assigning a function to an object the this keyword is set to the object when the function is invoked. This is true only for unbound function references. If the function reference is bound to another object you will have to use the Function.bind() method to ensure this is set to the correct object.
Answer
Rather than use call each time just assign the function to the object. When the function is call the this is set to the object
gameController.avg = appController.avg; // unbound function reference.
// now when you need to get the average for gameController
// just call its avg function
gameController.avg(); // this is automatically set to gameControler
Better still create the function outside the controllers and assign them at creation time.
// define the avg and refer it to the unbound function that you will share
var avg = function () {
var sumOfScores = this.scores.reduce(function (prev, cur, index, array) {
return prev + cur;
});
this.avgScore = sumOfScores / this.scores.length;
console.log(this.avgScore);
}
// create the objects as normal
var gameController = {
scores: [20, 34, 55, 46, 77],
avgScore: 112,
players: [
{name: "Ruth", playerId: 100, age: 22},
{name: "Shawnee", playerId: 101, age: 21}
],
avg:avg // avg is automatically bound to gameController when it is called
};
var appController = {
scores: [900, 845, 809, 950],
avgScore: null,
avg:avg // avg is automatically bound to appController when it is called
};
// or if you want to get really lazy.
var otherController = {
scores: [900, 900, 900, 900],
avgScore: null,
avg // avg is automatically bound to otherController when it is called
// and it is automatically named avg as well
};
appController.avg(); // 46.4
gameController.avg(); // 876
otherController.avg(); // 900
And there are over half a dozen other ways to achieve the same thing.
Related
const bankAccounts = [
{
id: 1,
name: "Susan",
balance: 100.32,
deposits: [150, 30, 221],
withdrawals: [110, 70.68, 120],
},
{ id: 2, name: "Morgan", balance: 1100.0, deposits: [1100] },
{
id: 3,
name: "Joshua",
balance: 18456.57,
deposits: [4000, 5000, 6000, 9200, 256.57],
withdrawals: [1500, 1400, 1500, 1500],
},
{ id: 4, name: "Candy", balance: 0.0 },
{ id: 5, name: "Phil", balance: 18, deposits: [100, 18], withdrawals: [100] },
];
function getClientWithGreatestBalance(bankAccounts) {
const maxAccount = bankAccounts[0];
const newArr = [];
for (let i = 0; i < array.length; i++) {
if (bankAccounts[i].balance > maxAccount.balance)
newArr.push([i]);
}
return newArr;
}
I am trying to loop through the bankAccounts array to get the object that has the greatest balance and push that object into the new array and return that single account.
I know I need to check the balance of each account but for some reason can not figure out how to get the single object.
I guess that array variable you are using in the for loop is meant to be bankAccounts, and at first it seemed weird to me that you are returning an array then I thought that you may have wanted to return an array in case there are multiple accounts that share the max balance. So, with that in mind your function should be something like this
function getClientWithGreatestBalance(bankAccounts) {
let maxBalance = 0;
let maxBalanceAcounts = [];
for (let i = 0; i < bankAccounts.length; i++) {
if (bankAccounts[i].balance > maxBalance){
maxBalance = bankAccounts[i].balance;
maxBalanceAcounts = [];
maxBalanceAcounts.push(bankAccounts[i])
}
else if (bankAccounts[i].balance === maxBalance){
maxBalanceAcounts.push(bankAccounts[i])
}
}
return maxBalanceAcounts;
}
You can't know which is the biggest until you've seen all of them, so it doesn't make sense to push anything to your output array until the end.
Instead, you need to keep track of the account with the biggest balance of those you've seen so far. So instead of an array, you want a single variable to track that one.
In fact you already have that variable, maxAccount. So if you find an account with a bigger balance than maxAccount.balance, you want to set maxAccount to the new account, instead of the previous max.
Once you have finished the loop, then you can push that to your output array (assuming you even need an output array, since you are always just returningg one).
Other points about your code:
getClientWithGreatestBalance will crash if you give it an empty input array.
You are starting by assuming (temporarily) that the first account is the biggest - which is OK, but then you are starting a loop at the first account, so the first time round the loop is comparing the first account to itself, which is unnecessary (but won't cause any errors).
It's better to iterate over an array directly rather than by index. Again, your code isn't wrong in that respect, but over-complicated.
for (let account of array) {
// ...
}
I want to concat 2 objects into the first object, but making sure the array elements stay in the same x-positions, like:
object.name[x],
object.age[x].
Also, I don't want any duplicates to be overwritten. Every entry should be preserved.
var users = {
name : [joe , tim],
age : [20 , 21]
}
var usersTemp = {
name : [bob , joe],
age : [22 , 23]
}
Result should be saved into (expand) the existing 'users' object:
var users = {
name : [joe , tim , bob , joe],
age : [20 , 21 , 22 , 23 ]
}
PS: I'm new to javascript, and since I cant find any examples I am wondering if this approach even makes sense? The list is SUPER long (over 50k entries).
You can use Array Destructuring to achieve this task if you don't want to append in the original array.
var users = {
name: ["joe", "tim"],
age: [20, 21],
};
var usersTemp = {
name: ["bob", "joe"],
age: [22, 23],
};
const result = {
name: [...users.name, ...usersTemp.name],
age: [...users.age, ...usersTemp.age],
};
console.log(result);
If you want to add in the same array then assign the result in the same variable users
var users = {
name: ["joe", "tim"],
age: [20, 21],
};
var usersTemp = {
name: ["bob", "joe"],
age: [22, 23],
};
users = {
name: [...users.name, ...usersTemp.name],
age: [...users.age, ...usersTemp.age],
};
console.log(users);
If there are multiple properties in both object that need to merge
var users = {
name: ["joe", "tim"],
age: [20, 21],
};
var usersTemp = {
name: ["bob", "joe"],
age: [22, 23],
};
for (let key in users) {
users[key].push(...usersTemp[key]);
}
console.log(users);
You could make a function that takes two objects with arbitrary keys and builds your new object with the concatenated values. You can then grab the entries of one of your objects using Object.entries(), which will give you a [[key, value], ...] pair array representation of your object:
[
['name', ['joe', 'tim']], // <- inner key-value pair array
['age', [20, 21]]
]
Using this array, you can then use the .map() method on it, to convert each inner key-value pair into a new key-value pair array, where the key remains, but the value is a concatenated version of the current array value with its corresponding array value from the other object you want to merge with. Once you have your modified the entries array, you can use Object.fromEntries() on this array to build your new object from the entries array:
const users = { name: ['joe', 'tim'], age: [20, 21] };
const usersTemp = { name: ['bob', 'ted'], age: [22, 23] };
function concatObjVals(obj1, obj2) {
return Object.fromEntries(
Object.entries(obj1).map(([key, arr]) => [
key,
arr.concat(obj2[key])
])
);
}
console.log(concatObjVals(users, usersTemp));
This could be written in a more concise manner if you use arrow-functions:
const concatObjVals = (obj1, obj2) =>
Object.fromEntries(Object.entries(obj1).map(([key, arr]) => [key, arr.concat(obj2[key])]));
If you find it easier to understand, you can use a regular for..in loop instead of Object.entries() and .map() to build your new object. The below for..in loop will iterate through all the keys in your first object (ie: users), and add that key to a new object result. It will also set the value to be the current value of the key from the first object, concatenated with the array value from the second object for the current key:
const users = { name: ['joe', 'tim'], age: [20, 21] };
const usersTemp = { name: ['bob', 'ted'], age: [22, 23] };
function concatObjVals(obj1, obj2) {
const result = {};
for(const key in obj1) {
result[key] = obj1[key].concat(obj2[key]);
}
return result;
}
console.log(concatObjVals(users, usersTemp));
var users = {
name : ['joe' , 'tim'],
age : [20 , 21]
}
var usersTemp = {
name : ['bob' , 'joe'],
age : [22 , 23]
}
usersTemp.name.push(...users.name)
usersTemp.age.push(...users.age)
console.log(usersTemp)
// output
// { name: Array ["bob", "joe", "joe", "tim"], age: Array [22, 23, 20, 21] }
Hey so I'm working on a JS project, and I came across an issue where I am trying to add/merge 2 objects together. So basically, there is a base object:
{age: 0,
lvl: 123,
xp: 321}
So we have this, and I have another object coming in with
{age: 12,
lvl: 21}
The result I want is
{age: 12,
lvl: 144,
xp: 321}
But that could be easily achieved with just individual property addition. However, I want to come to a point where I don't know what properties the object has, yet they are still added. Oh and the property type will for sure be a number. Any ideas?
Edit:
Ok, I see I mis worded some stuff. What I meant by me not knowing which properties it has, I meant that I know that the object may have one-all properties of the first object, just that I don't know which ones the second one has and does have.
Loop through the keys of the second object and add them to the first:
const first = {
age: 0,
lvl: 123,
xp: 321
};
const second = {
age: 12,
lvl: 21
};
for (const key in second) {
first[key] = (first[key] || 0) + second[key];
}
console.log(first);
Read more about for...in loops here.
Write a function that makes a copy of the first object and adds the keys in:
function addProperties(firstObj, secondObj) {
const newObj = Object.assign({}, firstObj);
for (let key of Object.keys(secondObj)) {
if (newObj.hasOwnProperty(key)) {
newObj[key] += secondObj[key];
} else {
newObj[key] = secondObj[key];
}
}
return newObj;
}
const first = {
age: 0,
lvl: 123,
xp: 321
};
const second = {
age: 12,
lvl: 21
};
const result = addProperties(first, second);
console.log(result);
I have an array of objects that look something like this;
[
{Number: 5002000, Origin: 123456, Count: 128},
{Number: 5002300, Origin: 900231, Count: 52},
{Number: 5002022, Origin: 534323, Count: 269}
]
Now I'm trying to multiply the "Count" value with a value from a designated price pool.
Which looks something like this;
[
{Prefix: 50023, Price: 20},
{Prefix: 50020, Price: 10},
{Prefix: 5002, Price: 60},
]
Currently there's an horrendous for loop with if-statements.
for (var key in sData) {
if (sData[key].Origin.startsWith('50023')) {
sData[key].sum = (sData[key].Count * 20);
}
else if (sData[key].Origin.startsWith('50020')) {
sData[key].sum = (sData[key].Count * 10);
}
// continues...
}
startsWith is a function that simply checks if the value starts with the (value).
Is there already a function in JS to map two arrays of objects? (I'm also having issues with the logic since the "Prefix" value basically has to go from the top down as not to land on the default "5002"-prefix.)
You should use nested loops in this situation. Also switch to Array.forEach method.
sData.forEach(function(item_, key) {
prices.forEach(function(item) {
if (sData[key].Origin.startsWith(item.Prefix)) {
sData[key].sum = (sData[key].Count * item.Price);
}
});
})
Assuming that second array can be transformed into the hash:
var tiers = {
50023: {Prefix: 50023, Price: 20},
50020: {Prefix: 50020, Price: 10},
5002: {Prefix: 5002, Price: 60},
}
You may make it look like this:
for (var key in sData) {
var key = String(sData[key])
var keyIndex = key.slice(5)
if (tiers.hasOwnProperty(keyIndex)) {
var price = tiers[keyIndex].Price
sData[key].sum = (sData[key].Count * price)
} else {
// Fallback solution
}
}
Going further you may even think of some recursive solution for fallback.
I've been reading through an article titled Don’t Be Scared Of Functional Programming and there is a piece of code I'm having trouble understanding (pasted below). The code's purpose is to get an item from an array of objects called data. What I don't understand is how the function within the function works. Where is the item argument coming from when you invoke getItem()?
var data = [
{
name: "Jamestown",
population: 2047,
temperatures: [-34, 67, 101, 87]
},
{
name: "Awesome Town",
population: 3568,
temperatures: [-3, 4, 9, 12]
}
{
name: "Funky Town",
population: 1000000,
temperatures: [75, 75, 75, 75, 75]
}
];
function getItem(propertyName) {
// Return a function that retrieves that item, but don't execute the function.
// We'll leave that up to the method that is taking action on items in our
// array.
return function(item) {
return item[propertyName];
}
}
I Understand that JS allows functions to be passed as arguments because they are treated as “first-class objects" in JS, but I don't understand where that item argument would be coming from.
This is defining a function that will accept a parameter called item which can be used to return the propertyName element from the given item. It is the function that is then passed back to the caller of getItem. It would be used as follows:
var getName = getItem('name');
var result = getName(x);
Where x is a variable containing a property called 'name'
Maybe this helps a bit.
It utilized a partial application of the first parameter propertyName with Function.prototype.bind():
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
Small example with your data and function:
function getItem(propertyName, item) {
return item[propertyName];
}
var data = [{ name: "Jamestown", population: 2047, temperatures: [-34, 67, 101, 87] }, { name: "Awesome Town", population: 3568, temperatures: [-3, 4, 9, 12] }, { name: "Funky Town", population: 1000000, temperatures: [75, 75, 75, 75, 75] }],
// this returns a function with only item as parameter
getName = getItem.bind(null, 'name');
document.write('<pre>' + JSON.stringify(data.map(getName), 0, 4) + '</pre>');