Getting the average price of OPSkins pricelist via NodeJS - javascript

I want to somehow get the average price of every skin in this list
My question arises is how would one use the use dates and the prices to get the average price of 60 days?
In my idea I would parse the JSON for every item/object and get the dates and prices, then somehow loop through every date and add up the prices then divide by the days.
But I suppose my idea doesnt even need the dates but just the prices, though what difference does average and median give? Because I hear people use the word median for pricing rather than average.

const minimumDate = getMinimumDate();
for(let item in list){
let count = 0;
item['average'] = 0;
for(let date in item){
if(parse(date) >= minimumDate){
item.average += date.price;
count++;
}
}
item.average /= count;
}
getMinimumDate is a function that gives you a date based on today less N days, in your case 60 days, implement it.
parse is a function that parse string to a date.
The median is the value in the middle of the highest and the minimum values in a sorted list.
The average it's the most common value.
1,1,1,2,3,4,6
The average is 18 / 7 = 2'....
There are 7 numbers, the median is the number in the position 7/2 + 1, so our median is 2 due to it is in 4th position.
If the list length is pair take two numbers add them and divide:
1,1,2,3,4,6
Take the two middles, 2+3 and divide by 2, so the median is 2.5

Thanks to #jesusgn90 answer I figured it out and made it look like so:
const request = require("request");
var url = 'https://files.opskins.media/file/opskins-static/pricelist/578080.json';
request({
url: url,
json: true
}, (err, res, body) => {
if (!err && res.statusCode === 200) {
var cur = new Date(),
7daysbefore = cur.setDate(cur.getDate() - 7);
var parsed = JSON.parse(JSON.stringify(body));
for (let item in parsed) {
parsed[item]['average'] = 0;
count = 0;
for (date in parsed[item]) {
if(new Date(date) >= cur){
parsed[item].average += parsed[item][date].price;
count++;
}
}
parsed[item].average = Math.floor(parsed[item].average / count);
}
}
fs.writeFile("test.json", JSON.stringify(parsed), function(err) {
if(err) {
return console.log(err);
}
console.log("The file was saved!");
});
});

Related

Calculate number of data received in past 30 seconds

I have a websocket where i receive price of stock randomly like 300ms or sometimes 1 second.I want to calculate how many price I received in past 30 seconds only.
var arr = [];
function onReceive(price) {
var timestamp = Number(new Date());
arr[timestamp] = [];
arr[timestamp].push(price);
if (arrarr[timestamp].length > 1000) {
arr.shift();
}
}
Now I just want to count how many price is received in last 30 seconds , I cannot come up with any logic.
I tried something like slicing last 30 items in array and calculating difference between last time stamp and -30 timestamp , which tells me how much time it took to receive 30 price ticks ,but i dont know how to calculate how to find how many ticks received in past 30 seconds , any ideas please.thank you.
arr[timestamp][arr[timestamp].length-1].key-arr[timestamp][0].key;
Personally I would create some sort of named instance for a log item, holding the UNIX timestamp and the price.
To retrieve anything in the last X seconds, you'd get the current UNIX timestamp, subtract X * 1000 from it, and use .filter() do a reverse iteration to retrieve all items where the timestamp is greater than that.
EDIT: As Robby pointed out, there's no need to search through the entire array as the timestamps are guaranteed to be in increasing order. By iterating in reverse, we can exit the loop when we find the first result outside of the desired window.
var priceLog = [];
function PriceLogItem(price) {
this.price = price;
this.timestamp = new Date().getTime();
}
function onReceive(price) {
priceLog.push(new PriceLogItem(price));
if (priceLog.length > 1000) log.shift();
}
function getPriceLogsSince(secondsAgo) {
let millisecondsAgo = secondsAgo * 1000;
let time = new Date().getTime() - millisecondsAgo;
let result = [];
for (let i = priceLog.length - 1; i >= 0; i--) {
if (priceLog[i].timestamp >= time) result.push(priceLog[i]);
else break;
}
return result;
}
//Usage
let priceLogs = getPriceLogsSince(30); //Get all logs within past 30 seconds
console.log(priceLogs);

Comparing current and next element of array and returning time difference

This is my array. Its length is about 9000. This is what a small bit of it looks like:
foreach_arr = ["21:07:01.535", "21:07:01.535", "21:07:26.113"]
There are a few occurences where the times diff is greater than a minute, and that is when I want to grab those times. And later use those times to get certain indices from another array. i.e "array"
I'm also using moment.js for time parsing.
Expected result: array = [8127, 9375, 13166, 14182]
Actual result: array = [8127, 13166]
Can't seem to find the issue here, I am getting 2 results when im supposed to be getting 4.
If the whole array is needed for troubleshooting, ill add it if I can.
var xx = foreach_arr.length - 1;
for(var z = 0; z < xx; z++) {
var current_row = foreach_arr[z];
var next_row = foreach_arr[z + 1];
var msElapsedTime = moment(next_row,"HH:mm:ss.SSS").diff(moment(current_row, "HH:mm:ss.SSS")) / 1000;
if(msElapsedTime > 60) {
attempt_indices.push(foreach_arr[z]);
}
}
for(var x = 0; x < attempt_indices.length; x++) {
array.push(newdata.indexOf(attempt_indices[x]));
}
Since the OP doesn't really need my code anymore, I'm posting it here to remove the downvote as much as anything else :)
const foreach_arr = ["21:07:01.535", "21:07:01.535", "21:07:26.113", '22:01:01.000'];
let processedForeach_arr = [];
let gtOneMinuteDiff = [];
foreach_arr.forEach((elem1, index1) => {
// elem1.split(':') turns foreach_arr[0] into ['21', '07', '01.535']
const splitElementArray = elem1.split(':');
let timeInMs = 0;
// this changes ['21', '07', '01.535'] into [75600000, 420000, 1535]
splitElementArray.forEach((elem2, index2) => {
if (index2 === 0) { // elem2 is hours. 3.6M ms per hour.
timeInMs += parseFloat(elem2) * 60 * 60 * 1000;
} else if (index2 === 1) { // elem2 is minutes. 60K ms per minute.
timeInMs += parseFloat(elem2) * 60 * 1000;
} else if (index2 === 2) { // elem2 is seconds. 1K ms per second.
timeInMs += parseFloat(elem2) * 1000;
} else {
throw `Expected array element formatted like HH:MM:SS.ms. Error on
element ${elem1}.`;
}
});
processedForeach_arr.push(timeInMs);
let timeDiff = processedForeach_arr[index1 - 1] - processedForeach_arr[index1];
if (Math.abs(timeDiff) > 60000) {
gtOneMinuteDiff.push(timeDiff);
}
});
To get the difference in milliseconds between foreach_arr[n] and foreach_arr[n+1], this code will
split each element of foreach_arr into 3 strings (hours, minutes, and seconds + milliseconds)
run parseFloat on each of those values to convert them to a number
convert all numbers to milliseconds and add them together
compare each consecutive value and return the difference.
Ok, I got this far and my son needs me. I'll finish out the code asap but you might beat me to it, hopefully the instructions above help.
turns out my code wasn't wrong. Just my idea of the whole proccess.
array = [8127, 13166]
is what I initialy get. With this, I use indexOf on my other array to eventually get my array as expected:
var another_test_arr = [];
for(var v = 0; v < array.length ; v++) {
var find = foreach_arr.indexOf(attempt_indices[v]);
another_test_arr.push(array[v], newdata.indexOf(foreach_arr[find + 1]));
}
Result: array = [8127, 9375, 13166, 14182]

JavaScript Array - all index over 0 are empty after loop

I have an array of date/timestamps saved as seconds, I'm looking to count the number of items that are between two timestamps (all times are assumed to be at midnight). I've cut down a larger function to the section that the problem seems to reside, let me know if you need any additional code.
var fullDay = 86399;
var end = parseInt(dataArray[0]) + parseInt(fullDay);
for (var i = 0; i < numberOfDays; i++) {
dataSegments[i] = sensorEventTotal(dataArray, end, previousEnd);
previousEnd = end;
end = parseInt(end) + parseInt(fullDay);
}
console.log(dataSegments.toString());
SensorEventTotal:
function sensorEventTotal (dataArray, end, previousEnd){
var counter = 0;
$.each(dataArray, function(i, item) {
if (parseInt(item) < end && parseInt(item) > previousEnd) {
counter++;
}
});
previousEnd = end;
return counter;
}
What I'm trying to do is to take the first date/time stamp and add 24 hours (fullDay value is 24 hours in seconds), I'm then looking to use that "end time" as the start point for the next loop with another 24 hours added onto that and so on.
In the end I'd want an array where each index stores the number of occurrences for each day e.g. dataSegments = [23,123,32,34] - so 23 events on day one, 123 events on day two etc.
At the moment this is the result I'm getting for dataSegments:
115,0,0,0,0,0
EDIT:
Sample of data in dataArray:
1496077569,1496077568,1496077567,1496077564,1496077563,1496077562,1496072956
Full array:
1496077569,1496077568,1496077567,1496077564,1496077563,1496077562,1496072956,1496072955,1496072951,1496072950,1496072949,1496072948,1496072809,1496072805,1496072804,1496072803,1495815090,1495815089,1495815088,1495807282,1495807281,1495807280,1495807279,1495807277,1495807276,1495807275,1495807274,1495807273,1495807267,1495807266,1495807265,1495805409,1495805408,1495805407,1495805406,1495805381,1495805380,1495805379,1495803061,1495803060,1495803059,1495803059,1495803000,1495802999,1495802998,1495786283,1495786282,1495786281,1495728263,1495728262,1495728261,1495728258,1495728257,1495728256,1495727698,1495727697,1495727696,1495727695,1495727694,1495727693,1495727491,1495727490,1495727489,1495727486,1495727485,1495727484,1495724286,1495724285,1495724284,1495724279,1495724278,1495724277,1495720363,1495720358,1495720357,1495720356,1495719373,1495719372,1495719368,1495719367,1495719366,1495717302,1495717301,1495717299,1495717298,1495717297,1495717296,1495713310,1495713309,1495713308,1495713305,1495713304,1495713303,1495713303,1495707902,1495707901,1495707897,1495707896,1495707895,1495707615,1495707611,1495707610,1495707609,1495707608,1495704627,1495704626,1495704625,1495704623,1495704622,1495704621,1495704133,1495704132,1495704128,1495704127,1495704126
This is what I managed to come up with. I hope code is clear just from variable names alone, given that the logic is very similar to yours.
const SECONDS_IN_DAY = 24 * 3600;
let events = [1496077569,1496077568,1496077567,1496077564,1496077563,1496077562,1496072956,1496072955,1496072951,1496072950,1496072949,1496072948,1496072809,1496072805,1496072804,1496072803,1495815090,1495815089,1495815088,1495807282,1495807281,1495807280,1495807279,1495807277,1495807276,1495807275,1495807274,1495807273,1495807267,1495807266,1495807265,1495805409,1495805408,1495805407,1495805406,1495805381,1495805380,1495805379,1495803061,1495803060,1495803059,1495803059,1495803000,1495802999,1495802998,1495786283,1495786282,1495786281,1495728263,1495728262,1495728261,1495728258,1495728257,1495728256,1495727698,1495727697,1495727696,1495727695,1495727694,1495727693,1495727491,1495727490,1495727489,1495727486,1495727485,1495727484,1495724286,1495724285,1495724284,1495724279,1495724278,1495724277,1495720363,1495720358,1495720357,1495720356,1495719373,1495719372,1495719368,1495719367,1495719366,1495717302,1495717301,1495717299,1495717298,1495717297,1495717296,1495713310,1495713309,1495713308,1495713305,1495713304,1495713303,1495713303,1495707902,1495707901,1495707897,1495707896,1495707895,1495707615,1495707611,1495707610,1495707609,1495707608,1495704627,1495704626,1495704625,1495704623,1495704622,1495704621,1495704133,1495704132,1495704128,1495704127,1495704126];
events = events.reverse();
let midnight = events[0] - events[0] % SECONDS_IN_DAY; // midnight before the first event
const eventsPerDay = []; // results array
const nrDays = 7; // lets count events for one week
let daysCounted = 0, eventsChecked = 0;
while (daysCounted < nrDays) {
midnight += SECONDS_IN_DAY;
let currentEvent = events[eventsChecked];
let eventsInThisDay = 0;
while (currentEvent < midnight) {
eventsInThisDay++;
eventsChecked++;
currentEvent = events[eventsChecked];
}
eventsPerDay[daysCounted] = eventsInThisDay;
daysCounted++;
}
console.log(eventsPerDay);
Notice that I reverse the sample array before running my execution. That is because your sample starts at May 29 and ends at May 25, so it's running backwards in time.
I encourage you to try your own code on a reversed array, might very well be the case that your solution is correct.
If you do not want to reverse the array, you could "reverse the counting" by going from the latest midnight to the first midnight, subtracting 1 day on each iteration.
In my opininion (and coming example), you can do it a bit simpler. Also, I've noticed a little problem in your function: if your dataArray and number of days (let's call them N) were big, you would have to iterate over the data array N number of times. It could become inefficient. Luckily you can do it in one loop iteration:
let sensorEventTotal = (data, start, daysNum) => {
// We create array of length daysNum filled with 0's.
let days = new Array(daysNum);
days.fill(0);
for(let entry of data) {
// If below the start timestamp, continue loop.
if(entry < start) continue;
// We calculate which day it is.
let index = parseInt((entry - start) / fullDay);
// We check if the entry is not from days we do not count.
if(index < daysNum)
days[index]++;
}
return days;
}
Code with working examples: http://jsbin.com/lekiboruki/edit?js,console.
EDIT: You didn't mention if your dataArray is sorted. My answer would also work on unsorted arrays.

Using map reduce in javascript

I'm trying to use mapreduce to calculate monthly sales and graph with chart.js later.
Suppose I have a JSON response with fields, amount and date.
For my map function, I took out the month:
month = _.map([items[i].transactionDate], function(date) {
return {
lables: items[i].transactionDate.slice(5,7)
};
});
and for my reduce function ... well I didn't know what to do here.
sum = _.reduce([items[i].amt], function(memo, num) {
return memo + items[i].amt
});
I realized that this will eventually calculate total sum not per month. Problem is that I don't know how to relate the two functions properly.
Edit: Per request, my JSON :
"items": [
{
"_id": "56d1cf3704aee3c68d89cc09",
"amt": 5,
"transactionDate": "2016-02-27T16:30:47.561Z",
}
]
and what I'm trying to get out of this is sales per month on a graph. so far I've been able to project months and total sale but not sales per a specific month.
I think, simply make one loop with adding new variable
let result= {};
_.forEach(items, (item) => {
// get month
let month = item.transactionDate.slice(5,7);
// get amount
let amount = item.amt;
// check if we have this month in finaly object
if(month in finaly) {
// added amount
result[month] += amount;
} else {
// if this month doesn't exist added with inital value
result[month ] = amount;
}
});
When you can get all amount of certain month or get sum of all months
let allSum = _.reduce(result, (sum, amount) => sum += amount);
let amountInCertainMonth = result["01"];
map and reduce are functions of Array that iterate over the array for you
map returns a new array with the results of the mapping function for each element
var months = items.map ( item => item.transactionDate.slice(5,7) )
and reduce applies the reducing function to each element and an accumulator.
var sum = items.reduce( (accum, item) => accum + item.amt , 0);
I assume items[i] is an object that has .transactionsDate and .amt as two arrays with corresponding indices. If so, you can get sales per month and total sales within one reduce function by starting with { totalSales, months }.
var chartData = items[i].transactionDate.reduce(function(total, month, index) {
var monthlyData = {
label: month.slice(5, 7),
sales: items[i].amt[index]
};
total.months.push(monthlyData);
total.totalSales += item.amt;
return total;
}, {
totalSales: 0, months: []
});
// get total sales
var totalSales = chartData.totalSales;
// get sales for May starting at index 0
var salesForMay = chartdata.months[4];
Let me know if this is what you were looking for.
I know this is old, but here is how to use reduce as a group by
var results = items
.map(function(data){
return {"month": data.transactionDate.slice(0,7), "amt": data.amt};
})
.reduce(function(amounts, data){
if (!amounts.hasOwnProperty(data.month))
amounts[data.month] = data.amt;
else
amounts[data.month] = amounts[data.month] + data.amt;
return amounts;
}, {});

In javascript, how do I add a random amount to a user's balance while controlling how much gets given total?

I'm trying to make it to where when a user does a certain thing, they get between 2 and 100 units. But for every 1,000 requests I want it to add up to 3,500 units given collectively.
Here's the code I have for adding different amounts randomly to a user:
if (Math.floor(Math.random() * 1000) + 1 === 900) {
//db call adding 100
}
else if (Math.floor(Math.random() * 100) + 1 === 90) {
//db call adding 40
}
else if (Math.floor(Math.random() * 30) + 1 === 20) {
//db call adding 10
}
else if (Math.floor(Math.random() * 5) + 1 === 4) {
//db call adding 5
}
else {
//db call adding 2
}
If my math is correct, this should average around 4,332 units per 1,000 calls. But obviously it would vary and I don't want that. I'd also like it to add random amounts instead, as the units added in my example are arbitrary.
EDIT: Guys, Gildor is right that I simply want to have 3,500 units, and give them away within 1,000 requests. It isn't even entirely necessary that it always reaches that maximum of 3,500 either (I could have specified that). The important thing is that I'm not giving users too much, while creating a chance for them to win a bigger amount.
Here's what I have set up now, and it's working well, and will work even better with some tweaking:
Outside of call:
var remaining = 150;
var count = 0;
Inside of call:
count += 1;
if (count === 100) {
remaining = 150;
count = 0;
}
if (Math.floor(Math.random() * 30) + 1 === 20) {
var addAmount = Math.floor(Math.random() * 85) + 15;
if (addAmount <= remaining) {
remaining -= addAmount;
//db call adding addAmount + 2
}
else {
//db call adding 2
}
}
else if (Math.floor(Math.random() * 5) + 1 === 4) {
var addAmount1 = Math.floor(Math.random() * 10) + 1;
if (addAmount1 <= remaining) {
remaining -= addAmount1;
//db call adding addAmount1 + 2
}
else {
//db call adding 2
}
}
else {
//db call adding 2
}
I guess I should have clarified, I want a "random" number with a high likelihood of being small. That's kind of part of the gimmick, where you have low probability of getting a larger amount.
As I've commented, 1,000 random numbers between 2 and 100 that add up to 3,500 is an average number of 3.5 which is not consistent with random choices between 2 and 100. You'd have to have nearly all 2 and 3 values in order to achieve that and, in fact couldn't have more than a couple large numbers. Nothing even close to random. So, for this to even be remotely random and feasible, you'd have to pick a total much large than 3,500. A random total of 1,000 numbers between 2 and 100 would be more like 51,000.
Furthermore, you can't dynamically generate each number in a truly random fashion and guarantee a particular total. The main way to guarantee that outcome is to pre-allocate random numbers that add up to the total that are known to achieve that and then random select each number from the pre-allocated scheme, then remove that from the choice for future selections.
You could also try to keep a running total and bias your randomness if you get skewed away form your total, but doing it that way, the last set of numbers may have to be not even close to random in order to hit your total consistently.
A scheme that could work if you reset the total to support what it should be for actual randomness (e.g. to 51,000) would be to preallocated an array of 500 random numbers between 2 and 100 and then add another 500 numbers that are the complements of those. This guarantees the 51 avg number. You can then select each number randomly from the pre-allocated array and then remove it form the array so it won't be selected again. I can add code to do this in a second.
function RandResults(low, high, qty) {
var results = new Array(qty);
var limit = qty/2;
var avg = (low + high) / 2;
for (var i = 0; i < limit; i++) {
results[i] = Math.floor((Math.random() * (high - low)) + low);
//
results[qty - i - 1] = (2 * avg) - results[i];
}
this.results = results;
}
RandResults.prototype.getRand = function() {
if (!this.results.length) {
throw new Error("getRand() called, but results are empty");
}
var randIndex = Math.floor(Math.random() * this.results.length);
var value = this.results[randIndex];
this.results.splice(randIndex, 1);
return value;
}
RandResults.prototype.getRemaining = function() {
return this.results.length;
}
var randObj = new RandResults(2, 100, 1000);
// get next single random value
if (randObj.getRemaining()) {
var randomValue = randObj.getRand();
}
Working demo for a truly random selection of numbers that add up to 51,000 (which is what 1,000 random values between 2 and 100 should add up to): http://jsfiddle.net/jfriend00/wga26n7p/
If what you want is the following: 1,000 numbers that add up to 3,500 and are selected from between the range 2 to 100 (inclusive) where most numbers will be 2 or 3, but occasionally something could be up to 100, then that's a different problem. I wouldn't really use the word random to describe it because it's a highly biased selection.
Here's a way to do that. It generates 1,000 random numbers between 2 and 100, keeping track of the total. Then, afterwards it corrects the random numbers to hit the right total by randomly selected values and decrementing them until the total is down to 3,500. You can see it work here: http://jsfiddle.net/jfriend00/m4ouonj4/
The main part of the code is this:
function RandResults(low, high, qty, total) {
var results = new Array(qty);
var runningTotal = 0, correction, index, trial;
for (var i = 0; i < qty; i++) {
runningTotal += results[i] = Math.floor((Math.random() * (high - low)) + low);
}
// now, correct to hit the total
if (runningTotal > total) {
correction = -1;
} else if (runningTotal < total) {
correction = 1;
}
// loop until we've hit the total
// randomly select a value to apply the correction to
while (runningTotal !== total) {
index = Math.floor(Math.random() * qty);
trial = results[index] + correction;
if (trial >= low && trial <= high) {
results[index] = trial;
runningTotal += correction;
}
}
this.results = results;
}
This meets an objective of a biased total of 3,500 and all numbers between 2 and 100, though the probability of a 2 in this scheme is very high and the probably of a 100 in this scheme is almost non-existent.
And, here's a weighted random generator that adds up to a precise total. This uses a cubic weighting scheme to favor the lower numbers (the probably of a number goes down with the cube of the number) and then after the random numbers are generated, a correction algorithm applies random corrections to the numbers to make the total come out exactly as specified. The code for a working demo is here: http://jsfiddle.net/jfriend00/g6mds8rr/
function RandResults(low, high, numPicks, total) {
var avg = total / numPicks;
var i, j;
// calculate probabilities for each value
// by trial and error, we found that a cubic weighting
// gives an approximately correct sub-total that can then
// be corrected to the exact total
var numBuckets = high - low + 1;
var item;
var probabilities = [];
for (i = 0; i < numBuckets; i++) {
item = low + i;
probabilities[i] = avg / (item * item * item);
}
// now using those probabilities, create a steps array
var sum = 0;
var steps = probabilities.map(function(item) {
sum += item;
return sum;
});
// now generate a random number and find what
// index it belongs to in the steps array
// and use that as our pick
var runningTotal = 0, rand;
var picks = [], pick, stepsLen = steps.length;
for (i = 0; i < numPicks; i++) {
rand = Math.random() * sum;
for (j = 0; j < stepsLen; j++) {
if (steps[j] >= rand) {
pick = j + low;
picks.push(pick);
runningTotal += pick;
break;
}
}
}
var correction;
// now run our correction algorithm to hit the total exactly
if (runningTotal > total) {
correction = -1;
} else if (runningTotal < total) {
correction = 1;
}
// loop until we've hit the total
// randomly select a value to apply the correction to
while (runningTotal !== total) {
index = Math.floor(Math.random() * numPicks);
trial = picks[index] + correction;
if (trial >= low && trial <= high) {
picks[index] = trial;
runningTotal += correction;
}
}
this.results = picks;
}
RandResults.prototype.getRand = function() {
if (!this.results.length) {
throw new Error("getRand() called, but results are empty");
}
return this.results.pop();
}
RandResults.prototype.getAllRand = function() {
if (!this.results.length) {
throw new Error("getAllRand() called, but results are empty");
}
var r = this.results;
this.results = [];
return r;
}
RandResults.prototype.getRemaining = function() {
return this.results.length;
}
As some comments pointed out... the numbers in the question does not quite make sense, but conceptually there are two approaches: calculate dynamically just in time or ahead of time.
To calculate just in time:
You can maintain a remaining variable which tracks how many of 3500 left. Each time when you randomly give some units, subtract the number from remaining until it goes to 0.
In addition, to make sure each time at least 2 units are given, you can start with remaining = 1500 and give random + 2 units each time.
To prevent cases that after 1000 gives there are still balances left, you may need to add some logic to give units more aggressively towards the last few times. However it will result in not-so-random results.
To calculate ahead of time:
Generate a random list with 1000 values in [2, 100] and sums up to 3500. Then shuffle the list. Each time you want to give some units, pick the next item in the array. After 1000 gives, generate another list in the same way. This way you get much better randomized results.
Be aware that both approaches requires some kind of shared state that needs to be handled carefully in a multi-threaded environment.
Hope the ideas help.

Categories

Resources