Related
I have a array of Object like this:
data() {
return {
searchVariant: null,
variantList: ["red", "green", "blue"],
products: [
{
id: 1,
title: "adipisicing elit.",
description: "ipsa deleniti.",
variants: [
{ id: 1, color: "red", size: "xl", price: 150, inStock: 150 },
{ id: 2, color: "blue", size: "xl", price: 46, inStock: 4 },
{ id: 3, color: "gray", size: "sm", price: 50, inStock: 50 },
],
},
{
id: 2,
title: "amet consecteturt.",
description: "id quas perspiciatis deserunt.",
variants: [
{ id: 1, color: "red", size: "xl", price: 150, inStock: 150 },
{ id: 2, color: "blue", size: "xl", price: 46, inStock: 4 },
{ id: 3, color: "green", size: "sm", price: 50, inStock: 50 },
],
},
],
};
}
While I will select a variant like green in select-option, Row Number 2 will show in the table search list.
I am using Vuejs to do this:
queryResults() {
if(this.searchVariant) {
return this.products.filter((item)=> {
return item.variants.filter((variant) => {
return this.searchVariant.toLowerCase().split(' ').every(v => variant.color.toLowerCase().includes(v))
})
})
}
else{
return this.products;
}
}
You only need to check if every object in the array has some variant with the color matching your search:
const products = [
{
id: 1,
title: 'adipisicing elit.',
description: 'ipsa deleniti.',
variants: [
{id: 1, color: 'red', size: 'xl', price: 150, inStock: 150},
{id: 2, color: 'blue', size: 'xl', price: 46, inStock: 4},
{id: 3, color: 'gray', size: 'sm', price: 50, inStock: 50}
]
},
{
id: 2,
title: 'amet consecteturt.',
description: 'id quas perspiciatis deserunt.',
variants: [
{id: 1, color: 'red', size: 'xl', price: 150, inStock: 150},
{id: 2, color: 'blue', size: 'xl', price: 46, inStock: 4},
{id: 3, color: 'green', size: 'sm', price: 50, inStock: 50}
]
}
];
const searchVariant = 'green';
const result = products.filter(item =>
item.variants.some(variant =>
searchVariant.toLowerCase().includes(variant.color)
)
);
console.log(result);
You can try something like this
queryResults() {
if(this.searchVariant) {
return this.products.filter((item)=> {
item.variants.some(variant =>
variant.color.toLowerCase() === searchVariant.toLowerCase())
}
}
else{
return this.products;
}
}
I built a chart in web webpage using the JavaScript chart program cytoscape.js. It does not display anything at all. But it doesn't' give syntax errors either, other than a warning about taking over the mouse for zooming purposes.
Here is the code:
<style type="text/css">
#cy {
width: 90%;
height: 300px;
display: block;
}
</style>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.14.0/cytoscape.min.js"></script>
<script type="text/javascript">
var cy = cytoscape({
container: document.getElementById('cy'), // container to render in
style: [ // the stylesheet for the graph
{
selector: 'node',
style: {
'background-color': '#666',
'label': 'data(id)'
}
},
{
selector: 'edge',
style: {
'width': 3,
'line-color': '#ccc',
'target-arrow-color': '#ccc',
'target-arrow-shape': 'triangle'
}
}
],
// initial viewport state:
zoom: 1,
pan: { x: 0, y: 0 },
// interaction options:
minZoom: 1e-50,
maxZoom: 1e50,
zoomingEnabled: true,
userZoomingEnabled: true,
panningEnabled: true,
userPanningEnabled: true,
boxSelectionEnabled: true,
selectionType: 'single',
touchTapThreshold: 8,
desktopTapThreshold: 4,
autolock: false,
autoungrabify: false,
autounselectify: false,
// rendering options:
headless: false,
styleEnabled: true,
hideEdgesOnViewport: false,
textureOnViewport: false,
motionBlur: false,
motionBlurOpacity: 0.2,
wheelSensitivity: 1,
pixelRatio: 'auto'
});
let options = {
name: 'breadthfirst',
fit: true, // whether to fit the viewport to the graph
directed: false, // whether the tree is directed downwards (or edges can point in any direction if false)
padding: 30, // padding on fit
circle: false, // put depths in concentric circles if true, put depths top down if false
grid: false, // whether to create an even grid into which the DAG is placed (circle:false only)
spacingFactor: 1.75, // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
avoidOverlap: true, // prevents node overlap, may overflow boundingBox if not enough space
nodeDimensionsIncludeLabels: false, // Excludes the label when calculating node bounding boxes for the layout algorithm
roots: undefined, // the roots of the trees
maximal: false, // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)
animate: false, // whether to transition the node positions
animationDuration: 500, // duration of animation in ms if enabled
animationEasing: undefined, // easing of animation if enabled,
animateFilter: function (node, i) { return true; }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
ready: undefined, // callback on layoutready
stop: undefined, // callback on layoutstop
transform: function (node, position) { return position; } // transform a given node position. Useful for changing flow direction in discrete layouts
};
cy.layout(options);
var eles1 = cy.add([
{ group: 'nodes', data: { id: 'E1'} },
{ group: 'nodes', data: { id: 'E2'} },
{ group: 'nodes', data: { id: 'E3'} },
{ group: 'nodes', data: { id: 'E4'} },
{ group: 'nodes', data: { id: 'E5'} },
{ group: 'nodes', data: { id: 'E6'} },
{ group: 'nodes', data: { id: 'E7'} },
{ group: 'nodes', data: { id: 'E8'} } ]);
var eles2 = cy.add([
{ group: 'nodes', data: { id: 'LH1'} },
{ group: 'nodes', data: { id: 'RH1'} },
{ group: 'nodes', data: { id: 'LH2'} },
{ group: 'nodes', data: { id: 'LH3'} },
{ group: 'nodes', data: { id: 'RH2'} },
{ group: 'nodes', data: { id: 'LH4'} },
{ group: 'nodes', data: { id: 'RH3'} },
{ group: 'nodes', data: { id: 'RH4'} } ]);
var eles4 = cy.add([
{ group: 'edges', data: { id: 'edge0', source: 'E4', target: 'E5' } },
{ group: 'edges', data: { id: 'edge1', source: 'E6', target: 'E7' } },
{ group: 'edges', data: { id: 'edge2', source: 'LH1', target: 'E1' } },
{ group: 'edges', data: { id: 'edge3', source: 'LH1', target: 'RH1' } },
{ group: 'edges', data: { id: 'edge4', source: 'LH2', target: 'E4' } },
{ group: 'edges', data: { id: 'edge5', source: 'LH3', target: 'E4' } },
{ group: 'edges', data: { id: 'edge6', source: 'LH4', target: 'E6' } },
{ group: 'edges', data: { id: 'edge7', source: 'RH1', target: 'E2' } },
{ group: 'edges', data: { id: 'edge8', source: 'RH1', target: 'E3' } },
{ group: 'edges', data: { id: 'edge9', source: 'RH2', target: 'E5' } },
{ group: 'edges', data: { id: 'edge10', source: 'RH3', target: 'E7' } },
{ group: 'edges', data: { id: 'edge11', source: 'RH4', target: 'E8' } } ]);
</script>
<center>
<div id="cy" >
</div>
</center>
My code is actually generated in an asp.net page, but that should not make a difference.
You are adding the elements to the graph after the layout has been run. That's why all your nodes are piled up on the upper left corner.
You have two choices here:
Supply both the layout at the elements on the constructor.
Initialize the graph first, then add the elements (e.g. using cy.json()) and then tell cytoscape to run the layout with cy.layout(...).run().
Documentation for cy.layout() is here, and for cy.json() here.
The following snippet uses the first approach, but I have left the second one commented in case you cannot supply the elements on the constructor call (i.e. if you have to fetch them asynchronously).
var nodeArray = [
{ group: 'nodes', data: { id: 'E1'} },
{ group: 'nodes', data: { id: 'E2'} },
{ group: 'nodes', data: { id: 'E3'} },
{ group: 'nodes', data: { id: 'E4'} },
{ group: 'nodes', data: { id: 'E5'} },
{ group: 'nodes', data: { id: 'E6'} },
{ group: 'nodes', data: { id: 'E7'} },
{ group: 'nodes', data: { id: 'E8'} },
{ group: 'nodes', data: { id: 'LH1'} },
{ group: 'nodes', data: { id: 'RH1'} },
{ group: 'nodes', data: { id: 'LH2'} },
{ group: 'nodes', data: { id: 'LH3'} },
{ group: 'nodes', data: { id: 'RH2'} },
{ group: 'nodes', data: { id: 'LH4'} },
{ group: 'nodes', data: { id: 'RH3'} },
{ group: 'nodes', data: { id: 'RH4'} }
];
var edgeArray = [
{ group: 'edges', data: { id: 'edge0', source: 'E4', target: 'E5' } },
{ group: 'edges', data: { id: 'edge1', source: 'E6', target: 'E7' } },
{ group: 'edges', data: { id: 'edge2', source: 'LH1', target: 'E1' } },
{ group: 'edges', data: { id: 'edge3', source: 'LH1', target: 'RH1' } },
{ group: 'edges', data: { id: 'edge4', source: 'LH2', target: 'E4' } },
{ group: 'edges', data: { id: 'edge5', source: 'LH3', target: 'E4' } },
{ group: 'edges', data: { id: 'edge6', source: 'LH4', target: 'E6' } },
{ group: 'edges', data: { id: 'edge7', source: 'RH1', target: 'E2' } },
{ group: 'edges', data: { id: 'edge8', source: 'RH1', target: 'E3' } },
{ group: 'edges', data: { id: 'edge9', source: 'RH2', target: 'E5' } },
{ group: 'edges', data: { id: 'edge10', source: 'RH3', target: 'E7' } },
{ group: 'edges', data: { id: 'edge11', source: 'RH4', target: 'E8' } }
];
var stylesArray = [{
selector: 'node',
style: {
'background-color': '#666',
'label': 'data(id)'
}
},{
selector: 'edge',
style: {
'width': 3,
'line-color': '#ccc',
'target-arrow-color': '#ccc',
'target-arrow-shape': 'triangle'
}
}];
var layout = {name: 'breadthfirst'};
// You used mostly default options, there's no need to redefine them
var cy = cytoscape({
container: document.getElementById('cy'),
style: stylesArray,
// If you want to apply the layout on the constructor
// you must supply the elements too
layout: layout,
elements: {
nodes: nodeArray,
edges: edgeArray
}
});
// Or add the elements afterwards, it's your choice
// But then you need to re-run the layout
/*
cy.json({
elements: {
nodes: nodeArray,
edges: edgeArray
}
});
// Tell cytoscape to apply the layout when ready
cy.ready(function () {
cy.layout(layout).run();
});
*/
#cy {
width: 90%;
height: 300px;
display: block;
}
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.14.0/cytoscape.min.js"></script>
<div id="cy"></div>
I have an array of objects that need to be filtered through, and displayed depending on which checkbox is selected. I can get it to work with a one dimension array, when I nest deeper I don't understand how to get everything working again.
This is the initial function that filters the array:
computed: {
selectedFilters: function() {
let filters = [];
let checkedFilters = this.shopFilters.filter(obj => obj.checked);
checkedFilters.forEach(element => {
filters.push(element.value);
});
return filters;
}
}
Tis is the data it pulls from:
shopFilters: [
{
name: 'price',
categories: [
{
checked: false,
value: 'Under $50'
},
{
checked: false,
value: '$50 to $100'
},
{
checked: false,
value: '$100 to $150'
},
{
checked: false,
value: '$150 to $200'
},
{
checked: false,
value: 'Over $200'
},
]
},
{
name: 'sports',
categories: [
{
checked: false,
value: 'lifestyle'
},
{
checked: false,
value: 'running'
},
{
checked: false,
value: 'basketball'
},
{
checked: false,
value: 'football'
},
{
checked: false,
value: 'soccer'
},
{
checked: false,
value: 'training & gym'
},
{
checked: false,
value: 'skateboarding'
},
{
checked: false,
value: 'baseball / softball'
},
{
checked: false,
value: 'golf'
}
]
}
]
This is the function that filters through the product data in another file to display on the page:
methods: {
getfilteredData: function() {
this.filteredData = data;
let filteredDataByfilters = [];
// first check if filters where selected
if (this.selectedFilters.length > 0) {
filteredDataByfilters= this.filteredData.filter(obj => this.selectedFilters.every(val => obj.indexOf(val) >= 0));
this.filteredData = filteredDataByfilters;
}
}
}
What the data looks like:
const data = [
{
name: 'SNKR 001',
gender: 'Men',
price: 100,
sport: 'running',
width: 'Wide',
colors: ['black', 'white', 'green', 'pink'],
sizes: [3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7, 7.5, 8, 8.5, 9, 9.5, 10, 10.5, 11, 11.5, 12, 12.5, 13, 14, 15],
image: '../assets/images/shoe-1.png'
},
{
name: 'SNKR 002',
gender: 'Men',
price: 100,
sport: 'running',
width: 'Wide',
colors: ['black', 'white', 'green', 'pink'],
sizes: [3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7, 7.5, 8, 8.5, 9, 9.5, 10, 10.5, 11, 11.5, 12, 12.5, 13, 14, 15],
image: '../assets/images/shoe-1.png'
},
{
name: 'SNKR 003',
gender: 'Men',
price: 100,
sport: 'training & gym',
width: 'Wide',
colors: ['black', 'white', 'green', 'pink'],
sizes: [3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7, 7.5, 8, 8.5, 9, 9.5, 10, 10.5, 11, 11.5, 12, 12.5, 13, 14, 15],
image: '../assets/images/shoe-1.png'
},
{
name: 'SNKR 004',
gender: 'Men',
price: 100,
sport: 'lifestyle',
width: 'Wide',
colors: ['black', 'white', 'green', 'pink'],
sizes: [3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7, 7.5, 8, 8.5, 9, 9.5, 10, 10.5, 11, 11.5, 12, 12.5, 13, 14, 15],
image: '../assets/images/shoe-1.png'
}
];
export default data;
You can merge all filter into 1 array:
const categories = this.shopFilters.map(item => item.categories)
const flatFilters = [].concat.apply([], categories)
then the filter will become flat:
flatFilters: [{
checked: false,
value: 'Under $50'
},
{
checked: false,
value: '$50 to $100'
},
{
checked: false,
value: '$100 to $150'
},
{
checked: false,
value: '$150 to $200'
},
{
checked: false,
value: 'Over $200'
},
{
checked: false,
value: 'lifestyle'
},
{
checked: false,
value: 'running'
},
{
checked: false,
value: 'basketball'
},
{
checked: false,
value: 'football'
},
{
checked: false,
value: 'soccer'
},
{
checked: false,
value: 'training & gym'
},
{
checked: false,
value: 'skateboarding'
},
{
checked: false,
value: 'baseball / softball'
},
{
checked: false,
value: 'golf'
}
]
then use can apply old logic
computed: {
selectedFilters: function() {
let filters = [];
const categories = this.shopFilters.map(item => item.categories)
const flatFilters = [].concat.apply([], categories)
let checkedFilters = flatFilters.filter(obj => obj.checked);
checkedFilters.forEach(element => {
filters.push(element.value);
});
return filters;
}
I have an array of products, each product has a price. I want to create a filter to calculate the total price of all products. The problem is that I can't use forEach() since I'm in a callback function. My question is, is there a function that does something like myArray.(intheobject).price, or a way to manage the callback and get the right results?
this.productList = [
{
type: 'chocolate',
pack: '3',
price: 5,
checkState: false
},
{
type: 'chocolate',
pack: '5',
price: 7,
checkState: false
},
{
type: 'chocolate',
pack: '10',
price: 10,
checkState: false
},
{
type: 'honey',
pack: '3',
price: 5,
checkState: false
},
{
type: 'honey',
pack: '5',
price: 7,
checkState: false
},
{
type: 'honey',
pack: '10',
price: 10,
checkState: false
},
{
type: 'candy',
pack: '3',
price: 5,
checkState: false
},
{
type: 'candy',
pack: '5',
price: 7,
checkState: false
},
{
type: 'candy',
pack: '10',
price: 10,
checkState: false
}
]
.filter('calculateTotal', function(){
var totalCost = 0;
return function(input){
return totalCost + ???
}
})
After PierreDuc's answer, my filter is this:
.filter('calculateTotal', function(){
return function(input){
return input.reduce((total, item) => item.price + total, 0);
}
})
You can use the reduce method for this:
const totalPrice = [{
type: 'chocolate',
pack: '3',
price: 5,
checkState: false
},
{
type: 'chocolate',
pack: '5',
price: 7,
checkState: false
}].reduce((total, item) => item.price + total, 0);
Filter is used to, like the name suggests, filter the current array based on a the return value of the provided method.
With reduce you can transform your array based on an input, 0 in this case. This gets assigned to the total parameter of the passed method, where the item is every item in your array. The value you return from that method will be the new value for the total parameter.
The filter callback can access each element. So you need to create a global variable, outside the callback function and add the value of each element in the callback function to that global variable. See filter documentation.
this.totalCost = 0;
this.productList = [
{
type: 'chocolate',
pack: '3',
price: 5,
checkState: false
},
{
type: 'chocolate',
pack: '5',
price: 7,
checkState: false
},
{
type: 'chocolate',
pack: '10',
price: 10,
checkState: false
},
{
type: 'honey',
pack: '3',
price: 5,
checkState: false
},
{
type: 'honey',
pack: '5',
price: 7,
checkState: false
},
{
type: 'honey',
pack: '10',
price: 10,
checkState: false
},
{
type: 'candy',
pack: '3',
price: 5,
checkState: false
},
{
type: 'candy',
pack: '5',
price: 7,
checkState: false
},
{
type: 'candy',
pack: '10',
price: 10,
checkState: false
}
]
var self = this;
angular.module('myReverseFilterApp', [])
.filter('calculateTotal', function(){
return function(input) {
self.totalCost += input.price;
}
})
You can then access the totalCost variable in your html. It is not possbile to return the totalCost from a filter function becaus this function gets applied to each element of the array. It is possible that the syntax is not correct, but I guess you get the gist.
const productList = [
{
type: 'chocolate',
pack: '3',
price: 5,
checkState: false
},
{
type: 'chocolate',
pack: '5',
price: 7,
checkState: false
},
{
type: 'chocolate',
pack: '10',
price: 10,
checkState: false
},
{
type: 'honey',
pack: '3',
price: 5,
checkState: false
},
{
type: 'honey',
pack: '5',
price: 7,
checkState: false
},
{
type: 'honey',
pack: '10',
price: 10,
checkState: false
},
{
type: 'candy',
pack: '3',
price: 5,
checkState: false
},
{
type: 'candy',
pack: '5',
price: 7,
checkState: false
},
{
type: 'candy',
pack: '10',
price: 10,
checkState: false
}
]
let totalPrice = productList.reduce(function(sum, currentValue, currentIndex, array) {
return sum+currentValue['price']
},0);
console.log(totalPrice)
You can do as following, First get the list of all the prices and store it in an array, and then use the reduce function to perform the action on prices
also read the documentation of reduce function:
reduce documentation
let pricesList = [];
productList.forEach(product => {
pricesList.push(product['price']);
})
let sumPrice = pricesList.reduce((priceA, priceB) => priceA + priceB, 0);
I'm trying to have different chart on each level using HighCharts.
For example-
Level-1 contains Bar graph.
Level-2 contains Pie graph.
Level-3 contains Table representation.
Below is my code snippet :
// Create the chart
$('#container').highcharts({
chart: {
type: 'pie' // Mentioned only pie.Need different graph on individual level.
},
title: {
text: 'Basic drilldown'
},
xAxis: {
type: 'category'
},
plotOptions: {
series: {
borderWidth: 1,
dataLabels: {
enabled: true,
}
}
},
series: [{
id: 'toplevel',
name: 'Animals',
data: [
{name: 'Cats', y: 4, drilldown: 'level1'}, //Level-1
{name: 'Dogs', y: 2},
{name: 'Cows', y: 1},
{name: 'Sheep',y: 2},
{name: 'Pigs', y: 1}
]
}],
drilldown: {
series: [ {
id:'level1',
name: 'Level 1',
data: [
{name: 'Trees', y: 1},
{name: 'Plants', y: 2},
{name: 'Grass', y: 3},
{name: 'Deeper Level-2', y: 4, drilldown: 'level2'} //Level-2
]
},{
id:'level2',
name: 'Level 2',
data: [
{name: 'Green', y:1},
{name: 'Red', y:2},
{name: 'Blue', y:3},
{name: 'Deeper Level-3', y: 4, drilldown: 'level3'} //Level-3
]
},{
id: 'level3',
name: 'Level 3',
data: [
{name:'Violet', y:1},
{name:'Red',y:2},
{name:'Yellow', y:3}
]
}]
}
})
I'm expecting that, while click on each level;Data is to be appeared with different chart on each individual level.
Above code is giving me data in same chart type on slice click which is supposed to be in different chart type.
To change each individual level chart type you should use series.type like this:
drilldown: {
series: [ {
id:'level1',
name: 'Level 1',
type: 'bar',
//^^^^^^^^^^^
data: [
{name: 'Trees', y: 1},
{name: 'Plants', y: 2},
{name: 'Grass', y: 3},
{name: 'Deeper Level-2', y: 4, drilldown: 'level2'} //Level-2
]
},{
id:'level2',
name: 'Level 2',
type: 'pie',
//^^^^^^^^^^^^
...
}]
}
since you want to drill to table at last level and highcharts does not support such thing you should create a custom drill to table:
define a method to create custom table when chart drills to level-3
var createTable = function(data) {
$("#container").hide();
// remove the existing table
$('#here_table .table').remove();
// create a table object
var table = $('<div class="table">back<table></table></div>').addClass('chart-table');
// iterate the series object, create rows and columnts
$.each(data, function( index, value ) {
var row = $('<tr></tr>').addClass('chart-row');
var col1 = $('<td></td>').text(value.name).appendTo(row);
var col2 = $('<td></td>').text(value.y).appendTo(row);
// mark the row of the clicked sector
table.append(row);
});
}
add point click event handler for level-2 points,call createTable method and pass the level-3 data to createTable:
{
id:'level2',
name: 'Level 2',
type: 'pie',
point: {
events: {
click: function () {
console.log(this);
if(this.name == "Deeper Level-3"){
var data = [
{name:'Violet', y:1},
{name:'Red',y:2},
{name:'Yellow', y:3}
];
createTable(data);
}
}
}
},
data: [
{name: 'Green', y:1},
{name: 'Red', y:2},
{name: 'Blue', y:3},
{name: 'Deeper Level-3', y: 4} //Level-3
]
}
and finally add an event handler for table back to get back where you were:
tableBack = function(){
$("#container").show();
// remove the existing table
$('#here_table .table').remove();
}
Working Fiddle of what I have done
hope that helps
You just need set type of the data series. see a jsfiddle
series: [{
name: 'IE distribution',
id: 'IE distribution',
type: 'bar',
data: [
['asia', 24.13],
['europe', 17.2],
['africa', 8.11]
]
}