I'm learning JS and I just finished a project, here the code:
const menu = {
_meal: "",
_price: 0,
//Let set the new value of meal only if it's a string.
set meal(mealToCheck) {
if (typeof mealToCheck === "string") {
return this._meal = mealToCheck;
}
},
//Let set the new value of price only if it's a number.
set price(priceToCheck) {
if (typeof priceToCheck === "number") {
return this._price = priceToCheck;
}
},
//If both setters are true, then return a message using them, otherwise return message saying the values are wrong.
get todaysSpecial() {
if (this._meal && this._price) {
return `Today's Special is ${this._meal}, for just ${this._price}£!!`;
} else {
return "Meal and Price wasn't entered correctly!!";
}
}
};
//Arrays for the meal options and respective prices, also a constant to get a random number in the array range.
const meals = ["Pizza", "Steak", "Pie", "Roast", "Moussaka", "Lasagne", "Tacos"];
const prices = [/*Pizza*/9, /*Steak*/13, /*Pie*/11, /*Roast*/14, /*Moussaka*/9, /*Lasagne*/10, /*Tacos*/9];
const random = Math.floor(Math.random() * meals.length);
//Assigns a new random value from the arrays. I used a single randomizer so that you can combine a plate to its price by the index number.
menu.meal = meals[random];
menu.price = prices[random];
//Check if the number of items in the meals and prices array it's equal, and if it is, creates the menu of the day string.
if (meals.length === prices.length) {
console.log(menu.todaysSpecial);
} else {
console.log("The number of prices and meals don't match!!");
}
At the end of the code I added a couple of arrays and a Math.random so that every time I run it, it gives me a different value.
Now I'm trying to find a way to have the random value give unique values until it reaches the array length, and then restart. At the moment I have 7 items in the array simulating the day of the week, I'd like to have each item coming out once per week and then reset.
I know how to do it by following the array index order, but I can't come out with a way to do it randomly, any input?
You can combine your prices and meals array to make it a bit simpler, then shuffle it.
const menu = {
_meal: "",
_price: 0,
//Let set the new value of meal only if it's a string.
set meal(mealToCheck) {
if (typeof mealToCheck === "string") {
return (this._meal = mealToCheck);
}
},
//Let set the new value of price only if it's a number.
set price(priceToCheck) {
if (typeof priceToCheck === "number") {
return (this._price = priceToCheck);
}
},
//If both setters are true, then return a message using them, otherwise return message saying the values are wrong.
get todaysSpecial() {
if (this._meal && this._price) {
return `Today's Special is ${this._meal}, for just ${this._price}£!!`;
} else {
return "Meal and Price wasn't entered correctly!!";
}
}
};
//Arrays for the meal options and respective prices, also a constant to get a random number in the array range.
let meals = [
{ name: "Pizza", price: 9 },
{ name: "Steak", price: 13 },
{ name: "Pie", price: 11 },
{ name: "Roast", price: 14 },
{ name: "Moussaka", price: 9 },
{ name: "Lasagne", price: 10 },
{ name: "Tacos", price: 9 }
];
// Used to shuffle your meals array
function shuffle(array) {
let currentIndex = array.length,
randomIndex;
// While there remain elements to shuffle.
while (currentIndex != 0) {
// Pick a remaining element.
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
// And swap it with the current element.
[array[currentIndex], array[randomIndex]] = [
array[randomIndex],
array[currentIndex]
];
}
return array;
}
meals = shuffle(meals);
// Iterate meals
for (let i = 0; i < meals.length; i++) {
menu.meal = meals[i].name;
menu.price = meals[i].price;
console.log(menu.todaysSpecial);
}
It is easy every time you pick value from the array remove it and when the array end just reset the array to the initial value
For not having probleme with rand nomber it should be like this
Rand=Math.random()*array.lenght;
Related
How could I create a Pagination System in JavaScript? There should be 10 products per page.
I have created an Array of products. I want to loop through all of these products and dsiplay the first 10 products on the first page, and then the next 10 products on the next page.
I have created this Array:
let products = {
data: [
{
productName: "Product1",
},
{
productName: "Product2",
},
{
productName: "Product3",
},
{
productName: "Product4",
},
{
productName: "Product5",
},
{
multiple other products
},
],
};
I have looped through all of the products and displayed them on screen like this:
for (let i of products.data) {
let card = document.createElement("div");
let name = document.createElement("h5");
container.appendChild(name);
card.appendChild(container);
document.getElementById("products").appendChild(card);
}
I want to do this in Vanilla JavaScript
Use the .slice method on the array to to get only a subset of what's within it. .slice(start, end) returns the items in the array starting with <start> and ending with <item before end>.
console.log([0,1,2,3,4,5].slice(2,4));
// returns [2,3]
You'd need additional code and buttons elsewhere on the page to keep track of the start position, but loop would be modified as follows:
for (let i of products.data.slice(start, start + 10)) {
I would make an array for each page:
let pages = new Object;
let pageNumber = 1;
for (let i = 0; i < products.data.length; i++) {
if(i === 0) {
pageNumber = 1
} else {
pageNumber = Math.ceil(i / 10);
}
if (Array.isArray(pages[`page${pageNumber}`])){
pages[`page${pageNumber}`].push(products.data[i]);
} else {
pages[`page${pageNumber}`] = new Array;
pages[`page${pageNumber}`].push(products.data[i]);
}
}
Change this:
for (let i of products.data) {
...
}
To this:
function displayPage(pageNumber) {
for (let i of pages[pageNumber]) {
...
}
}
Then I would run a for loop looping over the pages and making a button for each and displaying it when necessary. I would give each button an onclick that will call the
displayPage()
I’m trying to replicate a very simple function that I can get to work with arrays but not with objects. I just want to be able to run a function that logs the next object number as with the numbers array.
Take this working array as an example:
var numbers = [4,2,6],
count = 0;
incrementArr();
function incrementArr() {
if (count < numbers.length) { // if not last array element
console.log(numbers[count]);
count++;
} else {
console.log(numbers[0]);
count = 1;
}
}
Whenever you run the incrementArr function, it’ll just log the next number and then return to the start if the current state (count) is at the end.
However, I cannot replicate the same principle with this object list:
var objs = {
first: { // doesn't have to have 'first'
"number": 4
},
second: { // doesn't have to have 'second'
"number": 2
},
third: { // doesn't have to have 'third'
"number": 6
}
},
count = 0;
incrementObj();
function incrementObj() {
if (count < Object.keys(objs).length) { // if not last obj element
//objs["first"].number
console.log(objs[count].number);
count++;
} else {
console.log(objs["first"].number); // what if no "first" in objects?
count++;
}
}
How could the incrementObj function work the same way that the previous incrementArr function works?
It seems that I can’t pick the specific object instance (e.g. numbers[1] from the array would pick the 2nd number, but only objs[“second”].number would pick the 2nd object, which isn’t iterable if you know what I mean). How could I get a workaround for typical circumstances like this?
So essentially, what’s the difference between this:
first: { // doesn't have to have 'first'
"number": 4
}
and:
{ // doesn't have to have 'first'
"number": 4
}
Why have the "first" etc? (called the key?)
Is there generally a better way of going about object lists (it's difficult to explain)? Thanks for any advice here.
You could take a closure over the object and get the keys and store an index. The returned function get the value and increment and adjusts the index.
function increment(object) {
var keys = Object.keys(object),
index = 0;
return function() {
var value = object[keys[index]].number;
index++;
index %= keys.length;
return value;
};
}
var objs = { first: { number: 4 }, second: { number: 2 }, third: { number: 6 } },
incrementObj = increment(objs);
console.log(incrementObj());
console.log(incrementObj());
console.log(incrementObj());
console.log(incrementObj());
Try this, it access keys through the array generated from keys, objects are unordered list that means you will have to at least order the keys and access them in the array order.
const keysArr = Object.keys(objs);
function incrementObj() {
if (count < keysArr.length) { // if not last obj element
//
console.log(objs[keysArr[count]].number);
count++;
} else {
console.log(objs["first"].number); // what if no "first" in objects?
count++;
}
}
I propose using iterators
See this codepen
If your object have specific shapes, then you use this as a lens to find the number property you want. I'm not sure how you want to use the iterator and have return both the key and the value as separate properties, but you can as well return { [keys[nextIndex]]: values[nextIndex] } or find other shape (the world is your oyster).
Provided you go this length, why not try use RxJs to make your object an observable?
var objs = {
first: { // doesn't have to have 'first'
"number": 4
},
second: { // doesn't have to have 'second'
"number": 2
},
third: { // doesn't have to have 'third'
"number": 6
}
}
function propertyIterator(obj) {
const keys = Object.keys(obj)
const values = Object.values(obj)
const length = keys.length
let nextIndex = 0
return {
next: function() {
const value = {
key: keys[nextIndex],
value: values[nextIndex]
}
let done = false
if (nextIndex >= length) {
done = true
}
nextIndex += 1
return { current: value, done: done}
}
}
}
const incrementObj = propertyIterator(objs)
let result = incrementObj.next()
console.log(result.current.key, result.current.value.number || NaN)
result = incrementObj.next()
console.log(result.current.key, result.current.value.number || NaN)
result = incrementObj.next()
console.log(result.current.key, result.current.value.number || NaN)
using generators, see this codepen:
const objs = {
first: { // doesn't have to have 'first'
"number": 4
},
second: { // doesn't have to have 'second'
"number": 2
},
third: { // doesn't have to have 'third'
"number": 6
}
}
const inc = defaultValue => prop => function* (obj) {
for(let key in obj) {
yield obj[key][prop] || defaultValue
}
}
const getNumber = inc(NaN)('number')
const it = getNumber(objs)
let result = it.next()
while (!result.done) {
console.log(result.value)
result = it.next()
}
I have an array of objects. These objects have a property id. I need a function which returns the next available id (which is not used by an object).
array = [
{
id: 1
},
{
id: 2
},
{
id: 5
},
{
id: 3
}
]
I would like to have a function which takes an array as an input and returns a number (which is the next free id).
In the example case:
findFreeId(array){
magic happens
}
result --> 4
How about something like this?
function findFreeId (array) {
const sortedArray = array
.slice() // Make a copy of the array.
.sort(function (a, b) {return a.id - b.id}); // Sort it.
let previousId = 0;
for (let element of sortedArray) {
if (element.id != (previousId + 1)) {
// Found a gap.
return previousId + 1;
}
previousId = element.id;
}
// Found no gaps.
return previousId + 1;
}
// Tests.
let withGap = [{id: 1}, {id: 2}, {id: 5}, {id: 3}];
let noGap = [{id: 1}, {id: 2}];
let empty = [];
console.log(findFreeId(withGap)); // 4
console.log(findFreeId(noGap)); // 3
console.log(findFreeId(empty)); // 1
A simple approach is to get all the ID values, sort them, then starting at 0 look for the first missing number in the sequence. That may be OK where efficiency doesn't matter, but a more efficient method is to:
Get the IDs
Sort them
Step through the values to get the next available number
Insert the value in the list of IDs
Store the value so next time it starts at #3 from the previous value + 1
E.g.
class IDStore {
constructor(dataArray) {
if (!Array.isArray(dataArray)) {
return null;
}
this.previousIndex = 0;
this.indexes = dataArray.map(obj => obj.id).sort();
}
get nextIndex() {
while (this.indexes[this.previousIndex] == this.previousIndex) {
this.previousIndex++;
}
return this.previousIndex;
}
addIndex(index) {
if (!Number.isInteger(index) || this.indexes.find[index]) {
return null;
}
this.indexes.push(index);
this.indexes.sort();
return index;
}
}
var data = [ { id: 1 }, { id: 2 }, { id: 5 }, { id: 3 } ];
// Create an ID store
var idStore = new IDStore(data);
// Insert more objects in the array with unique IDs
for (var i=0, next; i<4; i++) {
// Current list of indexes
console.log('Indexes: ' + idStore.indexes);
// Get the next available index
next = idStore.nextIndex;
console.log('Next available: ' + next);
// Calling nextIndex doesn't affect next index
next = idStore.nextIndex;
console.log('Next available: ' + next);
// Use next index
data.push({id: next});
// Adding next index is manual
idStore.addIndex(next);
console.log('Added: ' + next);
}
// Data structure is independent
console.log('End: ' + JSON.stringify(data));
This is somewhat simplistic in that it assumes the IDs are sequential integers starting at 0 and doesn't have much validation or error handling.
Maintaining the id is separate from adding new members to the data array. It would be much better to combine the operations, so an "add object" method gets the next available ID, adds it to the object, adds the object to the array, updates the index and returns the new ID.
const findFreeId = (ids) => {
let id = 0;
for (id; ; id++) {
let isFree = true;
for (let i = 0; i < array.length; i++) {
const e = ids[i];
if (e === id) {
isFree = false;
break;
}
}
if (isFree) break;
}
return id;
}
I have an object, full of salmon. I want to loop through and check whether salmon is fresh or frozen. If frozen, put in no pile, if fresh, put in yes pile. If ounces is less than 2 and more than 6, no pile, and if it matches 2-6, yes pile.
Then, with the yes pile, I want to loop through those keys and values to find the cheapest one, and print that out.
Here is what I have so far, but I seem to be stuck on looping through the big object in order to run the checks.
How do I do this?
function findSalmon(salmonInStore){
var noPile,
yesPile;
var salmon = Object.keys(salmonInStore));
for(var key in salmonInStore){
if(salmonInStore.key.freshness !== 'fresh'){
noPile.push(salmonInStore.key);
} else {
yesPile.push(salmonInStore.key);
}
if(salmonInStore.key.ounces < 2 && salmonInStore.key.ounces > 6){
noPile.push(key);
}else {
yesPile.push(salmonInStore.key);
}
}
return yesPile;
}
var salmonInStore = {
salmon1 : {
ounces: 1,
freshness: 'frozen',
price: 16
},
salmon2 : {
ounces: 6,
freshness: 'fresh',
price: 10
},
salmon3 : {
ounces: 2,
freshness: 'frozen',
price: 20
},
salmon4 : {
ounces: 5,
freshness: 'fresh',
price: 1
},
salmon5 : {
ounces: 3,
freshness: 'frozen',
price: 12
}
}
findSalmon(salmonInStore);
First we can sort the salmon list by price.
const salmonList = Object.keys(salmonInStore)
.map(key => salmonInStore[key])
.sort((a,b) => {
if (a.price < b.price)
return -1;
if (a.price > b.price)
return 1;
return 0;
});
Then we can filter out the yes fish.
const yesPile = salmonList.filter((salmon) => {
return salmon.ounces >= 2 && salmon.ounces <= 6 && salmon.freshness === 'fresh';
});
yesPile[0] // cheapest fish
Check out the fiddle for a working example.
https://jsfiddle.net/y1jg01wy/
function findSalmon(salmonInStore){
var yesPile = [];
var noPile = [];
Object.keys(salmonInStore).forEach( (key)=> {
var salmon = salmonInStore[key]
if (salmon.freshness !== 'fresh' || salmon.ounces < 2 && salmon.ounces > 6){
noPile.push(key);
} else {
yesPile.push(key);
}
})
return yesPile;
}
I seem to be stuck on looping through the big object in order to run the checks.
Objects are also known as key/value pairs.
You can use Object.keys to get the keys of an object. From then on you need nested loops and dynamic value access using bracket notation to get the value of a k/v pair based on the key you got when you used Object.keys.
Here's an example:
var salmonInStore = {
salmon1 : {
ounces: 1,
freshness: 'frozen',
price: 16
},
salmon2 : {
ounces: 6,
freshness: 'fresh',
price: 10
}
}
Object.keys(salmonInStore).forEach(storeKey => {
var salmon = salmonInStore[storeKey]
// put per salmon logic here...
Object.keys(salmon).forEach(salmonKey => {
var value = salmon[salmonKey] // value for each key
console.log(value)
// put per salmon value logic here...
})
})
However if you plan on having arbitrary nesting levels - not just 1 level, you would need a recursive function to walk your salmons.
The above can be made more 'functional' by using reduce instead of forEach but that's another story.
The gist of the above is that you need Object.keys and someObj[key]. I've substituted your for.. loops with forEach just for brevity.
Here is my array.
donations = [
{
donateTo: "BaseCamp",
amount: 1000,
date: "12/19/2014, 08:40"
},
{
donateTo: "Where Most Needed",
amount: 3000,
date: "12/12/2014, 08:40"
},
{
donateTo: "Where Most Needed",
amount: 2000,
date: "12/11/2014, 08:40"
}
];
How can I return something like this? Where the donation with the donateTo that has the highest total donations is returned along with that total and the count of the gifts that made up that total.
{ donateTo: "Where Most Needed", total: 5000, count: 2}
I was previously able to get the results with MongoDB, but because I'm using Meteor, the aggregation is really ugly and not reactive. I'd rather fetch the cursor and then use a javascript function on the client side to get the data I need out.
Thanks
Here's an implementation using underscore:
var orgs = {};
_.each(donations, function(donation) {
if (orgs[donation.donateTo] == null)
orgs[donation.donateTo] = 0;
orgs[donation.donateTo] += donation.amount;
});
var amount = _.max(_.values(orgs));
var donateTo = _.invert(orgs)[amount];
var count = _.where(donations, {donateTo: donateTo}).length;
var result = {donateTo: donateTo, amount: amount, count: count};
console.log(result);
You can loop through the items and group them in an object, then loop through the properties in the object to find the largest amount:
var donations = [
{
donateTo: "BaseCamp",
amount: 1000,
date: "12/19/2014, 08:40"
},
{
donateTo: "Where Most Needed",
amount: 3000,
date: "12/12/2014, 08:40"
},
{
donateTo: "Where Most Needed",
amount: 2000,
date: "12/11/2014, 08:40"
}
];
var sums = {};
for (var i = 0; i < donations.length; i++) {
var donateTo = donations[i].donateTo;
if (sums.hasOwnProperty(donateTo)) {
sums[donateTo].amount += donations[i].amount;
sums[donateTo].count++;
} else {
sums[donateTo] = { donateTo: donateTo, amount: donations[i].amount, count: 1 };
}
}
var obj = null;
for (donateTo in sums) {
if (obj == null || sums[donateTo].amount > obj.amount) {
obj = sums[donateTo];
}
}
// show in Stackoverflow snippet
document.write(JSON.stringify(obj));
You can first iterate the documents to group them together by the donateTo field.
var m = {}; // to hold the grouped records by `donateTo`
donations.forEach(function(i){
if(!m[i["donateTo"]]){
m[i["donateTo"]] = {};
m[i["donateTo"]] = i;
m[i["donateTo"]]["count"] = 1;
}
else{
m[i["donateTo"]]["count"]++;
m[i["donateTo"]]["amount"] += i.amount;
}
});
And then find the group with the greatest amount:
var result = {amount:0}; // to hold the group with the largest amount
Object.keys(m).forEach(function(key){
if(m[key].amount > result.amount){
result = m[key];
}
})
delete result["date"];
console.log(result);
There is no super slick way to do this since you have to accumulate all the totals and then pick the highest one at the end and the very last entry in the array could completely change the highest value so you have to accumulate all the sub-totals as you go.
This function below accumulates a map of all the donateTo strings encountered and keeps a running total and cnt for each one. As it goes along, it then keeps track of which donateTo key has the highest total so far, saving an extra pass to compute the highest one.
function findHighestSum(list) {
// collect cumulative totals
var totals = {}, highestTotal = 0, highestKey;
list.forEach(function(item) {
var key = item.donateTo;
if (!totals[key]) {
totals[key] = {amount: item.amount, cnt: 1};
} else {
totals[key].amount += item.amount;
totals[key].cnt++;
}
if (totals[key].amount > highestTotal) {
highestTotal = totals[key].amount;
highestKey = key;
}
});
// { donateTo: "Where Most Needed", total: 5000, count: 2}
return {donateTo: highestKey, total: highestTotal, count: totals[highestKey].cnt};
}
Working demo: http://jsfiddle.net/jfriend00/7cfdsftz/
Because underscore is fun!
_.chain(donations)
.reduce(function(m, c) {
var findItem = _.findWhere(m, { 'donateTo': c.donateTo});
if (findItem) {
findItem.count++;
findItem.total += c.amount;
} else {
m.push({
'donateTo':c.donateTo,
'count':1,
'total':c.amount});
};
return m; }, [])
.max(function(k) { return k.total})
.value();
Using underscore...
// reduce donations, finally returning object with largest amount total
var highest = _.reduce(donations, function(memo, item, index){
// add to amount total if group exists, else init group
if(memo[item.donateTo]){
memo[item.donateTo].amount += item.amount;
memo[item.donateTo].count++;
} else {
memo[item.donateTo] = {
donateTo: item.donateTo,
amount:item.amount,
count:1
};
}
// if it is last iteration...
if(index === donations.length - 1){
// extract the max value
memo = _.max(_.toArray(memo), function(item){
return item.amount;
});
}
return memo;
}, {});
Using underscore js ...
// return the max (amount) of sorted, and collapsed array
var result = _.max( _.reduce( _.sortBy(donations, 'donateTo'), function(memo, item){
// if the last item has same key, update it (items are sorted by `donateTo`)
var _last = _.last(memo);
if(_last && _last.donateTo === item.donateTo){
_last.amount += item.amount;
_last.count++;
// otherwise create it
} else {
memo.push( _.extend( _.omit(item, 'date'), {'count':1} ) );
}
return memo
}, []), 'amount');
Underscore golf...
var h = {}, c={};
var result = _.max(donations, function(o){ var i = o.donateTo;
delete o.date; c[i] = ++c[i]||1; o.count=c[i];
return o.amount = h[i] = (h[i]||0)+o.amount
});