im using the current jquery ui slider with a range:
http://jqueryui.com/slider/#range
and the underscore.js
http://underscorejs.org/
so ive a minimum and a max which i send, after the user stopped to slide to a function:
currentSlide:function(){
$('#slider').slider({
range: true,
min: min,
max: max,
values: [ vmin, vmax ],
stop:$.proxy(this.afterSlide,this)
});
},
afterSlide:function(event, ui) {
console.log(ui.values[0]);
},
in the afterSlide function i get the min/max correctly, but i dont know how to use underscore.js to get all entries which have a price starting by this min and end by the max.
Examplearray:
var sample = {
"response": {
"things": [{
"index": 0,
"price": "10"
},{
"index": 1,
"price": "15"
},{
"index": 2,
"price": "60"
},{
"index": 3,
"price": "10"
},{
"index": 4,
"price": "100"
}
]
}
};
After im using the Slider i have the min/max [12,61] so i want to see all entries ONLY starting with a price between 12 and 61 $
var result = _.find(sample.response.things, min/max);??
I dont understand the _.range() function correctly to use it how i need, or to create a correct statement with the .where/.find() function.
I'd just get the max and min values, and since this seems rather trivial I'd just iterate the object and check if the relevant value is within the range, and add it to a new object, no _underscore needed really, something like this:
afterSlide:function(event, ui) {
var min = ui.values[0],
max = ui.values[1],
items = {};
$.each(sample.response.things[0], function(key, val) {
if (val.price > min && val.price < max) {
items[val.index] = val.price;
}
})
},
Related
I'm trying to create an object that should hold a range of values for a key which I would use as a look-up table. For example, it needs to "catch" values in the ranges: 500-524, 600-650, etc..
e.g.:
const numbers = {
500 to 524: "20",
600 to 650: "25"
}
And I would like to access the value in the following way:
user.list.map(list => numbers[user.points]).
I know I can put all the values from the range as keys but that would be highly inefficient:
const numbers = {
"500": "20",
"501": "20",
"502": "20",
"503": "20",
(...)
}
So, is it possible to include ranges somehow?
You could set it up like this:
numbers = [
{"start": 500, "end": 524, "value": "20"},
...
...
]
function getFromNumbers(num) {
for(let i=0; i<numbers.length; i++) {
numVal = numbers[i];
if(numVal.start <= num && numVal.end >= num)
return numVal.value;
}
}
console.log(user.list.map(list => getFromNumbers(user.points)))
Why don't you create an object like this:
const numbers =
[
{
start : 500
end : 524
value :20
},
{
start : 600
end : 650
value : 25
}
]
I think you should inverse the solution. You can create object, where key is "20" and value is array of values. For example:
const numbers = {
"20": _.range(500, 524)
}
I am working on a shopping basket feature using Vue JS, I need to create a subtotal feature which multiplies the price and quantity values of each item and adds them together to create a subtotal.
dataSet: [
{
"instock" : '',
"price": 49,
"qty": 1,
"subTotal": ''
},
{
"instock" : '',
"price": 29,
"qty": 1,
"subTotal" : '',
},
]
I have managed to return the values for each of the 'price' and 'qty' using forEach on the object array like so:
getSubTotal(dataSet) {
this.dataSet.forEach(function(data) {
console.log(data.price)
})
console
49
29
So far so good (I think), I am getting the price values returned to me, the only problem is that they are not being return in the same array but seperately. I really need them to be returned as [49,29] so I am able add the values together for the subtotal. I am able to hard code each array by index then add them seperately but this does not future proof the eventuality of a new clothing item in the object array.
Any advice/criticism or help would be greatly appreciated.
getSubTotal(dataSet) {
return this.dataSet.map(function(data) {
return data.price;
})
Okay so I was able to figure this out.
dataSet: [
{
"price": 49,
"qty": 1,
},
{
"price": 29,
"qty": 1,
},
]
subTotals(data) {
const calcArray = [data.price, data.qty];
return calcArray.reduce((a,b)=> {
//multiply price by quantity for subtotal
return a*b;
});
}
I'm trying the create a pie chart using d3pie.js user input values. Below is my code that is functional but takes random numbers on button click.
But I wan't the new segment value to be taken from user input field. I tried assigning the jquery function to a variable and then assigning that variable to value like below but that didn't work. I also tried to directly define the Jquery function to define value.
Attempt 1 (Didn't work):
var a = $("#first").val();
var num = 4;
$("#addData").on("click", function() {
adata.push({
label: num.toString(),
value: a
});
pie.updateProp("data.content", adata);
num++;
Attempt 2 (Didn't work):
adata.push({
label: num.toString(),
value: $("#first").val()
});
Below is my working code, would really appreciate some inputs from the folks on this.
var adata = [
{
"label": "JavaScript",
"value": 5,
},
{
"label": "Ruby",
"value": 3,
},
{
"label": "Java",
"value": 2,
}
];
--------------------
"data": {
"sortOrder": "value-desc",
"content": adata
--------------------
var num = 4;
$("#addData").on("click", function() {
adata.push({
label: num.toString(),
value: Math.floor(Math.random() * 10) + 1
});
pie.updateProp("data.content", adata);
num++;
--------------------
<input type="text" class="form-control" id="first">
<button type="button" class="btn btn-default" id="addData">Add Value</button>
Problem is because you are passing a string in value, it expects a number.
in your code
adata.push({
label: num.toString(),
value: a //incorrect it has to be a number.
});
need to do
adata.push({
label: num.toString(),
value: +$("#first").val() //make it a number so + is appended.
});
working code here
If I have an array that contains objects, how would I figure out which object occurs most frequently in that array?
Lets say the array is an array containing products, each product is an object with a unique id (unique per product not per element in the array):
const products = [
{
"itemId": "573412ab18c933d0085ca8b2",
"price": 20,
"cartQuantity": 1,
"itemName": "Tea"
},
{
"itemId": "573c82bd5c5ade1100532ec0",
"price": 100,
"cartQuantity": 1,
"itemName": "Carpet"
},
{
"itemId": "5734126218c933d0085ca8b0",
"_id": "57608d4187faf12708605360",
"price": 15,
"cartQuantity": 1,
"itemName": "Black Coffee"
},
{
"itemId": "573412dd18c933d0085ca8b3",
"_id": "57608d3d87faf12708605362",
"price": 50,
"cartQuantity": 1,
"itemName": "Nyquil"
},
{
"itemId": "573412ab18c933d0085ca8b2",
"price": 20,
"cartQuantity": 1,
"itemName": "Tea"
}
];
In the above snippet, you can see "Tea" occurs most frequently. I currently lack the algorithmic knowledge necessary to figure out how to pull the "Tea" object from that array as the most frequently occurring element. Also, if someone can help me here, it would be important to keep in mind an exception case where two elements in the array occur an equal amount of times and they "tie" for being the most frequent.
If this is an easy problem for someone to solve, please go easy on me and explain with as much detail as your patience permits how you solve this problem as I'm still learning. Feel free to answer with underscore or lodash functions!
You can format your output like :
[
{
"itemId": "573412ab18c933d0085ca8b2",
"ocurrences": 2
},
{
"itemId": "573c82bd5c5ade1100532ec0",
"ocurrences": 1
},
...
]
What is a sorted array where the first element is the most repeated in the array. As is sorted you can easily check if the second or the next elements have the same number of ocurrences .
To achieve this you need something like this :
let getOcurrences = array =>{
let ocurrences = {};
array.forEach(item=>{
if (!ocurrences[item.itemId])
ocurrences[item.itemId] = 1;
else
ocurrences[item.itemId] += 1;
});
return Object.keys(ocurrences)
.map(itemId => ({
itemId : itemId,
ocurrences : ocurrences[itemId]
}))
.sort((a, b)=> b.ocurrences - a.ocurrences);
};
const products = [
{
"itemId": "573412ab18c933d0085ca8b2",
"price": 20,
"cartQuantity": 1,
"itemName": "Tea"
},
{
"itemId": "573c82bd5c5ade1100532ec0",
"price": 100,
"cartQuantity": 1,
"itemName": "Carpet"
},
{
"itemId": "5734126218c933d0085ca8b0",
"_id": "57608d4187faf12708605360",
"price": 15,
"cartQuantity": 1,
"itemName": "Black Coffee"
},
{
"itemId": "573412dd18c933d0085ca8b3",
"_id": "57608d3d87faf12708605362",
"price": 50,
"cartQuantity": 1,
"itemName": "Nyquil"
},
{
"itemId": "573412ab18c933d0085ca8b2",
"price": 20,
"cartQuantity": 1,
"itemName": "Tea"
}
];
console.log(getOcurrences(products));
I think the easiest way would be to iterate through the array and adding each itemId of the current product to an object as a key as you go (with a value containing the count as 1, and the index in the array).
If the element is already in the object increment the count value by 1. In the end you just find the max count value in the object.
A solution:
function mostOccuringProduct(productList) {
const frequencies = {};
// iterating through each product counting the occurrence of each itemId
productList.forEach((product, i) => {
if (product.itemId in frequencies) {
frequencies[product.itemId].count++;
} else {
frequencies[product.itemId] = { count: 1, index: i };
}
})
// find max number of occurences of any 1 item
const maxOccurences = _.max(frequencies, item => item.count);
// get array of items that tie for this maxOccurences #
const itemsMatchingMaxOccurences = _.filter(frequencies, item => item.count === maxOccurences.count);
// create array of just the indicies of the items that tie for the maxNumber of occurences
const indicesOfMaxOccuringItems = _.map(itemsMatchingMaxOccurences, frequency => frequency.index);
// initialize an array to hold all items that tie for max occurencies
const products = [];
// push items to this array
indicesOfMaxOccuringItems.forEach(index => products.push(productList[index]));
// return an array of all products that occur most frequently.
// or just return the product that occurs most IF there is only one.
return products.length > 1 ? products : products[0];
}
This solution will return an array of products that tie for the most occurrences in the list. If there's only one item it will return 1 item.
I propose using a hashmap to count occurences while iterating over the products & keeping track of the most frequent product:
function getMostFrequent(array, identityFn) {
var counts = {}, max = 0, result;
array.forEach(element => {
var id = (identityFn && identityFn(element)) || element,
count = counts[id] = (counts[id] || 0) + 1;
if (count > max) {
max = count;
result = element;
}
});
return result;
}
const products = [{"itemId": 1}, {"itemId": 2}, {"itemId": 2}, {"itemId": 3}];
var mostFrequent = getMostFrequent(products, p => p["itemId"]);
console.log(mostFrequent);
If you want all products tied for the most frequent instead of just the first, declare result as Array and push elements on count == max.
Explanation of getMostFrequent():
We iterate over all elements in the given array and for each element we...
get its id via supplied identityFn callback function
initialize or increase the counter for this id by counts[id] = (counts[id] || 0) + 1;
and finally compare to the most frequent element so far and update if this one occurs more often.
Here is my O(n) solution. We create a hash table and keep the counts within the hash table in a separate property called indices which is an array. When an item gets it's first occurrence it's index value (in the products array) gets inserted at indices array index position 1. When another item gets it's first occurrence it overwrites index position 1. So when an item gets it's second occurrence it get's inserted at indices[2] and so on. At the end indices.pop() will give us the most frequent product's item's index and we are not morphing the products array.
var products = [
{
"itemId": "573412ab18c933d0085ca8b2",
"price": 20,
"cartQuantity": 1,
"itemName": "Tea"
},
{
"itemId": "573c82bd5c5ade1100532ec0",
"price": 100,
"cartQuantity": 1,
"itemName": "Carpet"
},
{
"itemId": "5734126218c933d0085ca8b0",
"_id": "57608d4187faf12708605360",
"price": 15,
"cartQuantity": 1,
"itemName": "Black Coffee"
},
{
"itemId": "573412dd18c933d0085ca8b3",
"_id": "57608d3d87faf12708605362",
"price": 50,
"cartQuantity": 1,
"itemName": "Nyquil"
},
{
"itemId": "573412ab18c933d0085ca8b2",
"price": 20,
"cartQuantity": 1,
"itemName": "Tea"
}
],
lut = products.reduce((p,c,i) => { ++p[c.itemId] || (p[c.itemId] = 1);
p.indices[p[c.itemId]] = i;
return p;
},{indices:[]});
console.log(lut);
console.log(products[lut.indices[lut.indices.length-1]]);
Looking for a way to make the following chart top out at 1 instead of 0.
I can set minimum: 1 in the valueAxes config section but that forces 1 to be the lower limit of what it will display regardless of where the line appears.
I don't see anything like this in the amcharts docs so I don't actually think its possible but I'm frequently wrong so I'm hoping thats the case.
There's no one-line solution to what you need.
Using minimum: 1 in combination with (or without) strictMinMax would force the scale to start at particular number, regardless of actual scope of the chart variables.
You can try a couple of more "involved" approaches.
Pre-calculate the minimum
Before the chart builds, cycle through the chart data and set minimum only if there are values close to it.
/**
* Create the chart
*/
var chart = AmCharts.makeChart("chartdiv", {
"type": "serial",
"theme": "light",
"path": "http://www.amcharts.com/lib/3/",
"dataProvider": [{
"year": "1950",
"value": 9
}, {
"year": "1951",
"value": 30
}, {
"year": "1952",
"value": 35
}, {
"year": "1953",
"value": 25
}, {
"year": "1954",
"value": 70
}, {
"year": "1955",
"value": 45
}, {
"year": "1956",
"value": 55
}],
"valueAxes": [{
"reversed": true,
// these are the made up properties that are used by our custom code
// set value axis minimum with "tentativeMinimum" if there are
// values withing "minimumThreshold" to it
"tentativeMinimum": 1,
"minimumThreshold": 9
}],
"graphs": [{
"id": "g1",
"bullet": "round",
"lineThickness": 2,
"type": "smoothedLine",
"valueField": "value"
}],
"categoryField": "year",
"categoryAxis": {
"labelsEnabled": false,
}
});
/**
* Add chart pre-processor
*/
AmCharts.addInitHandler( function( chart ) {
// check if there are any value axes defined
if ( typeof chart.valueAxes !== "object" || ! chart.valueAxes.length )
return;
// check if chart's value axis has "tentativeMinimum" set
// For the sake of simplicity we're just gonna take the first
// value axis and first graph.
// Normally we would want to check all value axes and their attached
// graphs to check for their respective values.
var axis = chart.valueAxes[0];
if ( axis.tentativeMinimum === undefined || axis.minimumThreshold === undefined )
return;
// get first charts valueField to check agains
var field = chart.graphs[0].valueField;
// cycle through the data
var min;
for ( var x = 0; x < chart.dataProvider.length; x++ ) {
if ( min === undefined || ( chart.dataProvider[x][field] < min ) )
min = chart.dataProvider[x][field];
}
// check if min is within the threshold
if ( min <= ( axis.tentativeMinimum + axis.minimumThreshold ) )
axis.minimum = axis.tentativeMinimum;
}, [ "serial" ] );
#chartdiv {
width: 400px;
height: 300px;
}
<script src="http://www.amcharts.com/lib/3/amcharts.js"></script>
<script src="http://www.amcharts.com/lib/3/serial.js"></script>
<script src="http://www.amcharts.com/lib/3/themes/light.js"></script>
<div id="chartdiv"></div>
Swap out "0" label for "1"
Use value axis' labelFunction to simply replace zero with 1:
var chart = AmCharts.makeChart("chartdiv", {
...
"valueAxes": [{
"reversed": true,
"labelFunction": function( label ) {
return label === 0 ? 1 : label;
}
}],
...
});
Please note, that this will not influence actual value axis scale. However, the difference might not be noticeable.