How to join two series before draw graph in highstocks? - javascript

I have two javascript data arrays in the following format,
series one: [{date:20170220,price:25},{date:20170221,price:25},......{date:20170322,price:25},{date:20170323,price:55}]
series two: [{date:20170220,price:30},{date:20170221,price:28},......{date:20170322,price:24},{date:20170323,price:23}]
two values are {Date,price}.We cannot guarantee that data is available for a specific date in both series. And I want to create a third series that includes the average price for each day. Is there a way to get the third series in highstocks. Appreciate any javascript base solution.

I think this snippet will address you through what you want.
Just looping through 2 arrays and creating third array. I left price key as undefined as your requirement.
var s1 = [{date:20170220, price: 25},{date: 20170221, price: 25},{date: 20170322, price: 25},{date: 20170325,price: 55}];
var s2 = [{date: 20170220,price: 30},{date: 20170221,price: 28},{date: 20170323, price: 24},{date: 20170324, price: 23}];
var sortHelper = function(a, b){return a.date - b.date}
//sorting them for sanity
s1.sort(sortHelper);
s2.sort(sortHelper);
var s1Index = 0, s2Index = 0, s3 = [];
while(s1Index < s1.length || s2Index < s2.length){
if(s1Index >= s1.length){ //for extreme conditions
for(;s2Index < s2.length; s2Index++){
s3.push({
date: s2[s2Index].date
});
}
break;
}
if(s2Index >= s2.length){ //for extreme conditions
for(;s1Index < s1.length; s1Index++){
s3.push({
date: s1[s1Index].date
});
}
break;
}
if(s1[s1Index].date === s2[s2Index].date){
s3.push({
date: s1[s1Index].date,
price: (s1[s1Index].price + s2[s2Index].price) / 2
});
s1Index++;
s2Index++;
}else if(s1[s1Index].date < s2[s2Index].date){
s3.push({
date: s1[s1Index].date
});
s1Index++;
}else if(s1[s1Index].date > s2[s2Index].date){
s3.push({
date: s2[s2Index].date
});
s2Index++;
}
}
console.log(s3);

Related

Javascript: Find out of sequence dates

Consider this nested array of dates and names:
var fDates = [
['2015-02-03', 'name1'],
['2015-02-04', 'nameg'],
['2015-02-04', 'name5'],
['2015-02-05', 'nameh'],
['1929-03-12', 'name4'],
['2023-07-01', 'name7'],
['2015-02-07', 'name0'],
['2015-02-08', 'nameh'],
['2015-02-15', 'namex'],
['2015-02-09', 'namew'],
['1980-12-23', 'name2'],
['2015-02-12', 'namen'],
['2015-02-13', 'named'],
]
How can I identify those dates that are out of sequence. I don't care if dates repeat, or skip, I just need the ones out of order. Ie, I should get back:
results = [
['1929-03-12', 'name4'],
['2023-07-01', 'name7'],
['2015-02-15', 'namex'],
['1980-12-23', 'name2'],
]
('Namex' is less obvious, but it's not in the general order of the list.)
This appears to be a variation on the Longest Increase Subsequence (LIS) problem, with the caveat that there may be repeated dates in the sequence but shouldn't ever step backward.
Use case: I have sorted and dated records and need to find the ones where the dates are "suspicious" -- perhaps input error -- to flag for checking.
NB1: I am using straight Javascript and NOT a framework. (I am in node, but am looking for a package-free solution so I can understand what's going on...)
Here's an adaptation of Rosetta Code LIS to take a custom getElement and compare functions. We can refine the comparison and element-get functions based on your specific needs.
function f(arr, getElement, compare){
function findIndex(input){
var len = input.length;
var maxSeqEndingHere = new Array(len).fill(1)
for(var i=0; i<len; i++)
for(var j=i-1;j>=0;j--)
if(compare(getElement(input, i), getElement(input, j)) && maxSeqEndingHere[j] >= maxSeqEndingHere[i])
maxSeqEndingHere[i] = maxSeqEndingHere[j]+1;
return maxSeqEndingHere;
}
function findSequence(input, result){
var maxValue = Math.max.apply(null, result);
var maxIndex = result.indexOf(Math.max.apply(Math, result));
var output = new Set();
output.add(maxIndex);
for(var i = maxIndex ; i >= 0; i--){
if(maxValue==0)break;
if(compare(getElement(input, maxIndex), getElement(input, i)) && result[i] == maxValue-1){
output.add(i);
maxValue--;
}
}
return output;
}
var result = findIndex(arr);
var final = findSequence(arr, result)
return arr.filter((e, i) => !final.has(i));
}
var fDates = [
['2015-02-03', 'name1'],
['2015-02-04', 'nameg'],
['2015-02-04', 'name5'],
['2015-02-05', 'nameh'],
['1929-03-12', 'name4'],
['2023-07-01', 'name7'],
['2015-02-07', 'name0'],
['2015-02-08', 'nameh'],
['2015-02-15', 'namex'],
['2015-02-09', 'namew'],
['1980-12-23', 'name2'],
['2015-02-12', 'namen'],
['2015-02-13', 'named'],
];
console.log(f(fDates, (arr, i) => arr[i][0], (a,b) => a >= b));
This solution tries to get all valid sequences and returns the longes sequences for filtering the parts out.
It works by iterating the given array and checks if the values could build a sequence. If a value is given, which part result has a valid predecessor, the array is appended with this value. If not a backtracking is made and a sequence is searched with a valid predecessor.
act. array
value 7 3 4 4 5 1 23 7 comment
----- ------------------------ ---------------------------
7 7 add array with single value
3 7 keep
3 add array with single value
4 7 keep
3 4 add value to array
4 7 keep
3 4 4 add value to array
5 7 keep
3 4 4 5 add value to array
1 7 keep
3 4 4 5 keep
1 add array with single value
23 7 23 add value to array
3 4 4 5 23 add value to array
1 23 add value to array
7 7 23 keep
7 7 fork above, filter for smaller or equal and add value
3 4 4 5 23 keep
3 4 4 5 7 fork above, filter for smaller or equal and add value
1 23 keep
1 7 fork above, filter for smaller or equal and add value
function longestSequences(array, getValue = v => v) {
return array
.reduce(function (sub, value) {
var single = true;
sub.forEach(function (s) {
var temp;
if (getValue(s[s.length - 1]) <= getValue(value)) {
s.push(value);
single = false;
return;
}
// backtracking
temp = s.reduceRight(function (r, v) {
if (getValue(v) <= getValue(r[0])) {
r.unshift(v);
single = false;
}
return r;
}, [value]);
if (temp.length !== 1 && !sub.some(s => s.length === temp.length && s.every((v, i) => getValue(v) === getValue(temp[i])))) {
sub.push(temp);
}
});
if (single) {
sub.push([value]);
}
return sub;
}, [])
.reduce(function (r, a) {
if (!r || r[0].length < a.length) {
return [a];
}
if (r[0].length === a.length) {
r.push(a);
}
return r;
}, undefined);
}
function notInSequence(array, getValue = v => v) {
var longest = longestSequences(array, getValue);
return array.filter((i => a => a !== longest[0][i] || !++i)(0));
}
var array = [7, 3, 4, 4, 5, 1, 23, 7, 8, 15, 9, 2, 12, 13],
fDates = [['2015-02-03', 'name1'], ['2015-02-04', 'nameg'], ['2015-02-04', 'name5'], ['2015-02-05', 'nameh'], ['1929-03-12', 'name4'], ['2023-07-01', 'name7'], ['2015-02-07', 'name0'], ['2015-02-08', 'nameh'], ['2015-02-15', 'namex'], ['2015-02-09', 'namew'], ['1980-12-23', 'name2'], ['2015-02-12', 'namen'], ['2015-02-13', 'named']],
usuallyFailingButNotHere = [['2015-01-01'], ['2014-01-01'], ['2015-01-02'], ['2014-01-02'], ['2015-01-03'], ['2014-01-03'], ['2014-01-04'], ['2015-01-04'], ['2014-01-05'], ['2014-01-06'], ['2014-01-07'], ['2014-01-08'], ['2014-01-09'], ['2014-01-10'], ['2014-01-11']],
test2 = [['1975-01-01'], ['2015-02-03'], ['2015-02-04'], ['2015-02-04'], ['2015-02-05'], ['1929-03-12'], ['2023-07-01'], ['2015-02-07'], ['2015-02-08']];
console.log(longestSequences(array));
console.log(notInSequence(array));
console.log(notInSequence(fDates, a => a[0]));
console.log(longestSequences(usuallyFailingButNotHere, a => a[0]));
console.log(notInSequence(usuallyFailingButNotHere, a => a[0]));
console.log(longestSequences(test2, a => a[0]));
console.log(notInSequence(test2, a => a[0]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
This solution uses the function reduce and keeps the previously accepted date to make the necessary comparisons.
var fDates = [['2015-02-03', 'name1'], ['2015-02-04', 'nameg'], ['2015-02-04', 'name5'], ['2015-02-05', 'nameh'], ['1929-03-12', 'name4'], ['2023-07-01', 'name7'], ['2015-02-07', 'name0'], ['2015-02-08', 'nameh'], ['2015-02-15', 'namex'], ['2015-02-09', 'namew'], ['1980-12-23', 'name2'], ['2015-02-12', 'namen'], ['2015-02-13', 'named']],
results = fDates.reduce((acc, c, i, arr) => {
/*
* This function finds a potential valid sequence.
* Basically, will check if any next valid sequence is
* ahead from the passed controlDate.
*/
function sequenceAhead(controlDate) {
for (var j = i + 1; j < arr.length; j++) {
let [dt] = arr[j];
//The controlDate is invalid because at least a forward date is in conflict with its sequence.
if (dt > acc.previous && dt < controlDate) return true;
}
//The controlDate is valid because forward dates don't conflict with its sequence.
return false;
}
let [date] = c; //Current date in this iteration.
if (i > 0) { // If this is not the first iteration
if (date === acc.previous) return acc; // Same as previous date are skipped.
// If the current date is lesser than previous then is out of sequence.
// Or if there is at least valid sequence ahead.
if (date < acc.previous || sequenceAhead(date)) acc.results.push(c);
else acc.previous = date; // Else, this current date is in sequence.
}
else acc.previous = date; // Else, set the first date.
return acc;
}, { 'results': [] }).results;
console.log(results);
.as-console-wrapper { max-height: 100% !important; top: 0; }
All of previous answers focus on JavaScript and maybe they won't work
correctly. So I decided to add new answer that focused on
Algorithm.
As #Trees4theForest mentioned in his question and comments, he is looking for a solution for Longest Increase Subsequence and out of order dates are dates that aren't in Longest Increase Subsequence (LIS) set.
I used this method like below. In algorithm's point of view, it's true.
function longestIncreasingSequence(arr, strict) {
var index = 0,
indexWalker,
longestIncreasingSequence,
i,
il,
j;
// start by putting a reference to the first entry of the array in the sequence
indexWalker = [index];
// Then walk through the array using the following methodolgy to find the index of the final term in the longestIncreasing and
// a sequence (which may need altering later) which probably, roughly increases towards it - http://en.wikipedia.org/wiki/Longest_increasing_subsequence#Efficient_algorithms
for (i = 1, il = arr.length; i < il; i++) {
if (arr[i] < arr[indexWalker[index]]) {
// if the value is smaller than the last value referenced in the walker put it in place of the first item larger than it in the walker
for (j = 0; j <= index; j++) {
// As well as being smaller than the stored value we must either
// - be checking against the first entry
// - not be in strict mode, so equality is ok
// - be larger than the previous entry
if (arr[i] < arr[indexWalker[j]] && (!strict || !j || arr[i] > arr[indexWalker[j - 1]])) {
indexWalker[j] = i;
break;
}
}
// If the value is greater than [or equal when not in strict mode) as the last in the walker append to the walker
} else if (arr[i] > arr[indexWalker[index]] || (arr[i] === arr[indexWalker[index]] && !strict)) {
indexWalker[++index] = i;
}
}
// Create an empty array to store the sequence and write the final term in the sequence to it
longestIncreasingSequence = new Array(index + 1);
longestIncreasingSequence[index] = arr[indexWalker[index]];
// Work backwards through the provisional indexes stored in indexWalker checking for consistency
for (i = index - 1; i >= 0; i--) {
// If the index stored is smaller than the last one it's valid to use its corresponding value in the sequence... so we do
if (indexWalker[i] < indexWalker[i + 1]) {
longestIncreasingSequence[i] = arr[indexWalker[i]];
// Otherwise we need to work backwards from the last entry in the sequence and find a value smaller than the last entry
// but bigger than the value at i (this must be possible because of the way we constructed the indexWalker array)
} else {
for (j = indexWalker[i + 1] - 1; j >= 0; j--) {
if ((strict && arr[j] > arr[indexWalker[i]] && arr[j] < arr[indexWalker[i + 1]]) ||
(!strict && arr[j] >= arr[indexWalker[i]] && arr[j] <= arr[indexWalker[i + 1]])) {
longestIncreasingSequence[i] = arr[j];
indexWalker[i] = j;
break;
}
}
}
}
return longestIncreasingSequence;
}
With method above, we can find dates that is out of order like below:
// Finding Longest Increase Subsequence (LIS) set
var _longestIncreasingSequence = longestIncreasingSequence(fDates.map(([date]) => date));
// Out of order dates
var result = fDates.filter(([date]) => !_longestIncreasingSequence.includes(date));
Online demo(jsFiddle)
here is a simple self- explanatory solution. hope it will help you.
const findOutOfSequenceDates = items => {
items = items.map(d => d);
const sequence = [], outOfsequence = [];
sequence.push(items.shift());
const last = ind => sequence[sequence.length - ind][0];
items.forEach(item => {
const current = new Date(item[0]);
if (current >= new Date(last(1))) {
sequence.push(item);
} else if (current >= new Date(last(2))) {
outOfsequence.push(sequence.pop());
sequence.push(item);
} else {
outOfsequence.push(item);
}
});
return outOfsequence;
};
var fDates = [
['2015-02-03', 'name1'],
['2015-02-04', 'nameg'],
['2015-02-04', 'name5'],
['2015-02-05', 'nameh'],
['1929-03-12', 'name4'],
['2023-07-01', 'name7'],
['2015-02-07', 'name0'],
['2015-02-08', 'nameh'],
['2015-02-15', 'namex'],
['2015-02-09', 'namew'],
['1980-12-23', 'name2'],
['2015-02-12', 'namen'],
['2015-02-13', 'named'],
];
console.log(findOutOfSequenceDates(fDates));
Use the Javascript Date type. Compare with those objects. Very simplistically,
date1 = new Date(fDates[i, 0])
date2 = new Date(fDates[i+1, 0])
if (date2 < date1) { // or whatever comparison you want ...
// flag / print / alert the date
}
To clarify, This merely finds items out of sequence. You can do that with strings, as Jaromanda X pointed out. However, you use the phrase "way out of line"; whatever this means for you, Date should give you the ability to determine and test for it. For instance, is '2023-07-01' unacceptable because it's 8 years away, or simply because it's out of order with the 2015 dates? You might want some comparison to a simpler time span, such as one month, where your comparison will looks something like
if (date2-date1 > one_month)
Summary of your question
If I have understood your question correctly, you are trying to identify array entries that do not follow a chronological order based on the time/date property value.
Solution
Convert the date string / time into a UNIX time stamp (number of seconds lapsed since 01/jan/1970 at 00:00:00)
Using a loop, we can store the value against a previous reading per itenary, if the value is negative, this would indicate an error in the date lapse, if the value is positive, it would indicate the order is valid
When negative, we can create an array to denote the position of the reference array and its values allowing you to go back to the original array and review the data.
Example Code
var arrData = [
{date: '2015-02-03', value:'name1'},
{date: '2015-02-04', value:'nameg'},
{date: '2015-02-04', value:'name5'},
{date: '2015-02-05', value:'nameh'},
{date: '1929-03-12', value:'name4'},
{date: '2023-07-01', value:'name7'},
{date: '2015-02-07', value:'name0'},
{date: '2015-02-08', value:'nameh'},
{date: '2015-02-15', value:'namex'},
{date: '2015-02-09', value:'namew'},
{date: '1980-12-23', value:'name2'},
{date: '2015-02-12', value:'namen'},
{date: '2015-02-13', value:'named'}
];
var arrSeqErrors = [];
function funTestDates(){
var intLastValue = 0, intUnixDate =0;
for (x = 0; x <= arrData.length-1; x++){
intUnixDate = Date.parse(arrData[x].date)/1000;
var intResult = intUnixDate - intLastValue;
if (intResult < 0){
console.log("initeneration: " + x + " is out of sequence");
arrSeqErrors.push (arrData[x]);
}
intLastValue = intResult;
}
console.log("Items out of sequence are:");
console.log(arrSeqErrors);
}
funTestDates();

How to choose a weighted random array element in Javascript?

For example: There are four items in an array. I want to get one randomly, like this:
array items = [
"bike" //40% chance to select
"car" //30% chance to select
"boat" //15% chance to select
"train" //10% chance to select
"plane" //5% chance to select
]
Both answers above rely on methods that will get slow quickly, especially the accepted one.
function weighted_random(items, weights) {
var i;
for (i = 1; i < weights.length; i++)
weights[i] += weights[i - 1];
var random = Math.random() * weights[weights.length - 1];
for (i = 0; i < weights.length; i++)
if (weights[i] > random)
break;
return items[i];
}
I replaced my older ES6 solution with this one as of December 2020, as ES6 isn't supported in older browsers, and I personally think this one is more readable.
If you'd rather use objects with the properties item and weight:
function weighted_random(options) {
var i;
var weights = [options[0].weight];
for (i = 1; i < options.length; i++)
weights[i] = options[i].weight + weights[i - 1];
var random = Math.random() * weights[weights.length - 1];
for (i = 0; i < weights.length; i++)
if (weights[i] > random)
break;
return options[i].item;
}
Explanation:
I've made this diagram that shows how this works:
This diagram shows what happens when an input with the weights [5, 2, 8, 3] is given. By taking partial sums of the weights, you just need to find the first one that's as large as a random number, and that's the randomly chosen item.
If a random number is chosen right on the border of two weights, like with 7 and 15 in the diagram, we go with the longer one. This is because 0 can be chosen by Math.random but 1 can't, so we get a fair distribution. If we went with the shorter one, A could be chosen 6 out of 18 times (0, 1, 2, 3, 4), giving it a higher weight than it should have.
Some es6 approach, with wildcard handling:
const randomizer = (values) => {
let i, pickedValue,
randomNr = Math.random(),
threshold = 0;
for (i = 0; i < values.length; i++) {
if (values[i].probability === '*') {
continue;
}
threshold += values[i].probability;
if (threshold > randomNr) {
pickedValue = values[i].value;
break;
}
if (!pickedValue) {
//nothing found based on probability value, so pick element marked with wildcard
pickedValue = values.filter((value) => value.probability === '*');
}
}
return pickedValue;
}
Example usage:
let testValues = [{
value : 'aaa',
probability: 0.1
},
{
value : 'bbb',
probability: 0.3
},
{
value : 'ccc',
probability: '*'
}]
randomizer(testValues); // will return "aaa" in 10% calls,
//"bbb" in 30% calls, and "ccc" in 60% calls;
Here's a faster way of doing that then other answers suggested...
You can achieve what you want by:
dividing the 0-to-1 segment into sections for each element based on their probability (For example, an element with probability 60% will take 60% of the segment).
generating a random number and checking in which segment it lands.
STEP 1
make a prefix sum array for the probability array, each value in it will signify where its corresponding section ends.
For example:
If we have probabilities: 60% (0.6), 30%, 5%, 3%, 2%. the prefix sum array will be: [0.6,0.9,0.95,0.98,1]
so we will have a segment divided like this (approximately): [ | | ||]
STEP 2
generate a random number between 0 and 1, and find it's lower bound in the prefix sum array. the index you'll find is the index of the segment that the random number landed in
Here's how you can implement this method:
let obj = {
"Common": "60",
"Uncommon": "25",
"Rare": "10",
"Legendary": "0.01",
"Mythical": "0.001"
}
// turning object into array and creating the prefix sum array:
let sums = [0]; // prefix sums;
let keys = [];
for(let key in obj) {
keys.push(key);
sums.push(sums[sums.length-1] + parseFloat(obj[key])/100);
}
sums.push(1);
keys.push('NONE');
// Step 2:
function lowerBound(target, low = 0, high = sums.length - 1) {
if (low == high) {
return low;
}
const midPoint = Math.floor((low + high) / 2);
if (target < sums[midPoint]) {
return lowerBound(target, low, midPoint);
} else if (target > sums[midPoint]) {
return lowerBound(target, midPoint + 1, high);
} else {
return midPoint + 1;
}
}
function getRandom() {
return lowerBound(Math.random());
}
console.log(keys[getRandom()], 'was picked!');
hope you find this helpful.
Note:
(In Computer Science) the lower bound of a value in a list/array is the smallest element that is greater or equal to it. for example, array:[1,10,24,99] and value 12. the lower bound will be the element with value 24.
When the array is sorted from smallest to biggest (like in our case) finding the lower bound of every value can be done extremely quickly with binary searching (O(log(n))).
Here is a O(1) (constant time) algo to solve your problem.
Generate a random number from 0 to 99 (100 total numbers). If there are 40 numbers (0 to 39) in a given sub-range, then there is a 40% probability that the randomly chosen number will fall in this range. See the code below.
const number = Math.floor(Math.random() * 99); // 0 to 99
let element;
if (number >= 0 && number <= 39) {
// 40% chance that this code runs. Hence, it is a bike.
element = "bike";
}
else if (number >= 40 && number <= 69) {
// 30% chance that this code runs. Hence, it is a car.
element = "car";
}
else if (number >= 70 && number <= 84) {
// 15% chance that this code runs. Hence, it is a boat.
element = "boat";
}
else if (number >= 85 && number <= 94) {
// 10% chance that this code runs. Hence, it is a train.
element = "train";
}
else if (number >= 95 && number <= 99) {
// 5% chance that this code runs. Hence, it is a plane.
element = "plane";
}
Remember this, one Mathematical principle from elementary school? "All the numbers in a specified distribution have equal probability of being chosen randomly."
This tells us that each of the random numbers have equal probability of occurring in a specific range, no matter how large or small that range might be.
That's it. This should work!
I added my solution as a method that works well on smaller arrays (no caching):
static weight_random(arr, weight_field){
if(arr == null || arr === undefined){
return null;
}
const totals = [];
let total = 0;
for(let i=0;i<arr.length;i++){
total += arr[i][weight_field];
totals.push(total);
}
const rnd = Math.floor(Math.random() * total);
let selected = arr[0];
for(let i=0;i<totals.length;i++){
if(totals[i] > rnd){
selected = arr[i];
break;
}
}
return selected;
}
Run it like this (provide the array and the weight property):
const wait_items = [
{"w" : 20, "min_ms" : "5000", "max_ms" : "10000"},
{"w" : 20, "min_ms" : "10000", "max_ms" : "20000"},
{"w" : 20, "min_ms" : "40000", "max_ms" : "80000"}
]
const item = weight_random(wait_items, "w");
console.log(item);
ES2015 version of Radvylf Programs's answer
function getWeightedRandomItem(items) {
const weights = items.reduce((acc, item, i) => {
acc.push(item.weight + (acc[i - 1] || 0));
return acc;
}, []);
const random = Math.random() * weights[weights.length - 1];
return items[weights.findIndex((weight) => weight > random)];
}
And ES2022
function getWeightedRandomItem(items) {
const weights = items.reduce((acc, item, i) => {
acc.push(item.weight + (acc[i - 1] ?? 0));
return acc;
}, []);
const random = Math.random() * weights.at(-1);
return items[weights.findIndex((weight) => weight > random)];
}
Sure you can. Here's a simple code to do it:
// Object or Array. Which every you prefer.
var item = {
bike:40, // Weighted Probability
care:30, // Weighted Probability
boat:15, // Weighted Probability
train:10, // Weighted Probability
plane:5 // Weighted Probability
// The number is not really percentage. You could put whatever number you want.
// Any number less than 1 will never occur
};
function get(input) {
var array = []; // Just Checking...
for(var item in input) {
if ( input.hasOwnProperty(item) ) { // Safety
for( var i=0; i<input[item]; i++ ) {
array.push(item);
}
}
}
// Probability Fun
return array[Math.floor(Math.random() * array.length)];
}
console.log(get(item)); // See Console.

JavaScript array find fitting range

I have the following array with two objects:
var myArr = [{
id: 3,
licences: 100
new_value_pr_licence: 40
}, {
id: 4,
licences: 200
new_value_pr_licence: 25
}]
A user wish to buy 150 licences. This means that they fall into the category 100 because they are above 100 licences but below 200 which means they pay $40 per licence.
Note that the array object values varies.
Order your plans by the price per licence:
myArr.sort(function (a, b) {
return a.new_value_pr_licence - b.new_value_pr_licence;
})
then starting from the start of the array, take as many of that plan as you can without going over the number the user wants to buy:
var numUserWants = 150;
var purchases = {};
var cheapestAvailableProduct = myArr.shift();
while (numUserWants > 0 && cheapestAvailableProduct) {
if (numUserWants <= cheapestAvailableProduct.licences) {
purchases[cheapestAvailableProduct.id] = Math.floor(cheapestAvailableProduct.licences / numUserWants);
numUserWants = cheapestAvailableProduct.licences % numUserWants;
}
cheapestAvailableProduct = myArr.shift();
}
At this point, purchases will now be a map of plan id to number:
purchases => {
3: 3
4: 1
}
This doesn't handle the case where over-purchasing is the cheapest option (eg: it's cheaper to buy 160 at 4x40, instead of 150 at 3x40 + 1x25 + 1x5), but it's probably a good start for you to tweaking.
Just a simple forEach here. Take the number requested, begin calculating/mutating total based on option limits, and once the number requested is less than the option limit you have your final total, which wont be mutated any longer and returned from the function.
function calculateDiscountedTotal(numberRequested, myArr){
var total;
// loop, compare, calculate
myArr.forEach(function(option) {
if(numberRequested >= option.licenses){
total = numberRequested * option.new_value_pr_licence
}
}
if(total != undefined){
return total;
} else {
// user never had enough for initial discount
return "no discount price";
}
}
Sort the array first in terms of number of licenses and then get the object in which number of licenses is less than number of licenses to be bought (just less than the next item in the array which is greater than number of licenses to be bought)
var myArr = [
{
id: 3,
licences: 100
new_value_pr_licence: 40,
},
{
id: 4,
licences: 200,
new_value_pr_licence: 25
},
];
var numOfLic = 150;
myArr.sort( function(a,b){ return a.licences - b.licences } );
var selectedObj = myArr.reduce( function(prev,current){
if ( current.licences > numOfLic )
{
return prev;
}
});
console.log ( "pricing should be " + ( selectedObj.new_value_pr_licence * numOfLic ) );

jQuery: how to generate 3 different random integers given limits/range?

I'm trying to generate 3 different random integers but I can't do that with jQuery, a little bit different in some ways comparing that with JS.
For example, I have 20 different questions to make in my app, but I only want to see 5 different questions, all of them random.
I look for it and I think I should use this function for that purpose:
$.randomBetween(minValue, maxValue);
but it only gives you one single number. How to get 3, between 1 and 20, being different each other?
This will give you [howMany] distinct random integers between [min] and [max]:
var howMany = 5;
var min = 1;
var max = 20;
var a = new Array();
while (a.length < howMany) {
var n = Math.floor(Math.random() * (max - min) + 0.5) + min;
if (a.indexOf(n) == -1) {
a.push(n);
}
}
a.sort(function(a, b){return a-b}); // optional sorting
console.log(a);
This will work:
var myArray = [];
for(var i = 0; i < 3; i++) {
var numberIsInArray = false;
var rand = generateRandomNumb(1, 21);
for(var j = 0; j < myArray.length; j++){
if(rand === myArray[j]) {
numberIsInArray = true;
i--;
}
}
if(!numberIsInArray){
myArray.push(rand);
}
}
/**
* Returns a random number between min (inclusive) and max (exclusive)
*/
function generateRandomNumb(min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
You save 3 different random between 1 - 20 i myArray. Now you can do what you whant with the numbers in the array.
use one array store each random generated value in it. then check next generated value is in array or not if yes then again generate if it is not in array then store it simple
Try
function randomRange(from, to, leng){
var tem, A= [], L= 0, i= 0;
randomRangeLoop:
while(L< leng){
tem= Math.floor(Math.random()*to)+from;
i= 0;
while(i<L){
if(A[i++]=== tem) continue randomRangeLoop;
}
A[L++]= tem;
}
return A;
}
alert(randomRange(1, 10, 5))
The code for shuffling the array of the 20 questions is simple and I'll place it here for easy C&P
var questions = [
'Question 1',
'Question 2',
'Question 3',
'Question 4',
'Question 5',
'Question 6',
'Question 7',
'Question 8',
'Question 9',
'Question 10',
'Question 11',
'Question 12',
'Question 13',
'Question 14',
'Question 15',
'Question 16',
'Question 17',
'Question 18',
'Question 19',
'Question 20'
];
function knuthShuffle(arr){
var idx, rand, tmp;
// start at the end
idx = arr.length - 1;
while (idx) {
/*
Take one place after another
reducing the number of places
every time such that every place
gets swapped at least once with a
randomly chosen other place
*/
rand = Math.floor(Math.random() * idx--);
// swap current place with random place
temp = arr[idx];
arr[idx] = arr[rand];
arr[rand] = temp;
}
return arr;
}
// take the first five elements of the array
var randomFiveQuestions = knuthShuffle(questions).slice(0,5);
// or take the last five ones
randomFiveQuestions = knuthShuffle(questions).slice(-5);
I use the normal implementation of the Knuth shuffle here, the first algorithm at the wiki-page.
Ah, way to late, as always ;-)
This solution demonstrates how to generate 3 random yet unique numbers. There are many ways to accomplish this. In my solution, I chose to store the collected numbers in a map. It is much more efficient to locate an element in a map O(1) than it is to determine if an element exists in a list O(n).
var numbers = {};
while (Object.keys(numbers).length < 3) { // Does the numbers map contains 3 keys if not find a random unique number
var num = Math.floor(Math.random() * 20) + 1; // generates random number between 1 and 20 inclusive
if (numbers[num] != "undefined") { // check if the number generated is already a key in the numbers map
numbers[num] = num; // add the generated number to the map
}
}
console.log(Object.keys(numbers));
I finally managed to do it with your help mates, using some "if" and "for" loops when necessary. Thank you all for your answers and help. Love this page and community.
function getRandomArbitrary(min, max) {
var limit=10;
var values=[];
for (var i=0; i<limit; i++) {
var value=Math.floor(Math.random()*(max-min)+min);
alert("Value number "+i+": "+value);
if (i==0){
values[i]=value;
alert("Initial value: "+values[i]);
}
else{
for (k=0;k<i;k++){
if(value==values[k]){
alert("Repeated: ");
k=i;
i--;
}
}
if (k==i){
values[i]=value;
alert("Value number "+i+" saved: "+values[i]);
}
}
}
//Checking array
for (var l=0; l<limit; l++){
alert("Values["+l+"]: "+values[l]);
}
}

Javascript - Remove values from array - Constructing an array of times

I am having an issue figuring this out. The idea is that I have an array of values that I'm considering times, and I need to remove all values that overlap with a specific time:
var availableTimes = ['0 - 0.5', '0.5 - 1', '1 - 1.5', '1.5 -2', '2 - 2.5', '2.5 -3']
var timeToRemove = '0.5 - 2'
I have full control over what the array looks like, but that's the idea. I can't figure out how to construct my array of available times and my timeToRemove so that I can accomplish this. I am using a 24:00 hour clock, so 1 - 1.5 is 1:00 AM to 1:30 AM.
Any thoughts on the best way to construct this?
I would prefer a OO way to do it:
(1) Construct an array with objects with following structure:
{
StartTime:[fload],
EndTime:[fload]
}
So your time array looks like this
var availableTimes=
[
{StartTime:0,EndTime:0.5},
{...}
];
(2) The time to be removed has the same structure:
var timeToRemove = '0.5 - 2';
=>
var timeToRemove={StartTime:0.5,EndTime:2};
(3) Delete algorithm looks like this:
for (var i=0;i<availableTimes.length;i++)
{
if(availableTimes[i].StartTime > timeToRemove.StartTime
&& availableTimes[i].EndTime < timeToRemove.EndTime)
availableTimes[i]=null; //perform deletion
}
var availableTimes = ['0 - 0.5', '0.5 - 1', '1 - 1.5', '1.5 -2', '2 - 2.5', '2.5 -3']
var timeToRemove = '0.5 - 2';
var tempar = timeToRemove.replace(/\s/g,"").split('-');
for(var i = 0, t = availableTimes.length; i < t; i++){
var itemar = availableTimes[i].replace(/\s/g,"").split('-');
if(tempar[0] === itemar[0] && tempar[1] === itemar[1]){
availableTimes .splice(i, 1);
return;
}
}
Since you have control over your data structures, I think you're better off structuring it as arrays of length 2 than strings (though you can convert what you have to this form by using split(" - "))
var availableTimes = [[0,0.5], [0.5,1], [1,1.5], [1.5 -2], [2,2.5], [2.5 -3]]
var timeToRemove = [0.5,2]
for (var i = 0; i<availableTimes.length;i++){
//use >= and <= if you want a closed interval
if ((availableTimes[i][0] > timeToRemove[0] &&
availableTimes[i][0] < timeToRemove[1]) ||
(availableTimes[i][1] > timeToRemove[0] &&
availableTimes[i][1] < timeToRemove[1]))
{
console.log("overlapping time: " + availableTimes[i]);
}
}
produces:
overlapping time: 0.5,1
overlapping time: 1,1.5

Categories

Resources