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'm struggling with manipulating a Javascript object and will appreciate your advice:
I have the following object:
const source = {
id: '1',
name: 'Customer A',
projects: [
{
id: '10',
name: 'Project 2',
description: 'Project 2 description',
products: [
{
id: '100',
name: 'Product 1',
vendor: 'Vendor 1',
instances: [
{
id: '1000',
operatingSystem: 'Microsoft Windows 2012R2',
environment: 'Prod',
version: '4.1',
notes: '',
},
],
},
{
id: '200',
name: 'Product 2',
vendor: 'Vendor 2',
instances: [
{
id: '2000',
operatingSystem: 'Microsoft Windows 2016',
environment: 'Prod',
version: '4.0',
notes: '',
},
],
},
],
},
{
id: '20',
name: 'Project 1',
description: 'Project 1 description',
products: [
{
id: '200',
name: 'Product 2',
vendor: 'Vendor 2',
instances: [
{
id: '2000',
operatingSystem: 'Microsoft Windows 2016',
environment: 'Prod',
version: '4.0',
notes: '',
},
{
id: '3000',
operatingSystem: 'RedHat Linux 7',
environment: 'Prod',
version: '3.12',
notes: '',
},
],
},
],
},
],
};
I would like to extract from the object above a list of instances grouped by products (this part is working fine):
const products = [
{
id: '100',
name: 'Product 1',
vendor: 'Vendor 1',
instances: [
{
id: '1000',
operatingSystem: 'Microsoft Windows 2012R2',
environment: 'Prod',
version: '4.1',
notes: '',
},
],
},
{
id: '200',
name: 'Product 2',
vendor: 'Vendor 2',
instances: [
{
id: '2000',
operatingSystem: 'Microsoft Windows 2016',
environment: 'Prod',
version: '4.0',
notes: '',
},
{
id: '2000',
operatingSystem: 'Microsoft Windows 2016',
environment: 'Prod',
version: '4.0',
notes: '',
},
{
id: '3000',
operatingSystem: 'RedHat Linux 7',
environment: 'Prod',
version: '3.12',
notes: '',
},
],
},
];
The above is achieved by mapping the projects, flattening the products-array, and reducing the results.
My next goal is to add each instance the projects it associated to. I need to attach the project id and project name. In the example above, you can see the instance with the '2000' id is associated with 2 projects, and therefore, the expected results should look like this:
const expected = [
{
id: '100',
name: 'Product 1',
vendor: 'Vendor 1',
instances: [
{
id: '1000',
operatingSystem: 'Microsoft Windows 2012R2',
environment: 'Prod',
version: '4.1',
notes: '',
projects: [
{
id: '10',
name: 'Project 2',
},
],
},
],
},
{
id: '200',
name: 'Product 2',
vendor: 'Vendor 2',
instances: [
{
id: '2000',
operatingSystem: 'Microsoft Windows 2016',
environment: 'Prod',
version: '4.0',
notes: '',
projects: [
{
id: '10',
name: 'Project 2',
},
{
id: '20',
name: 'Project 1',
},
],
},
{
id: '3000',
operatingSystem: 'RedHat Linux 7',
environment: 'Prod',
version: '3.12',
notes: '',
projects: [
{
id: '20',
name: 'Project 1',
},
],
},
],
},
];
I tried to manipulate the array by several 'forEach' loops, maps, and so on but with no success.
Would appreciate having your ideas with how it can be achieved.
const { inspect } = require('util'); // if Node.js
const source = {
id: '1', name: 'Customer A', projects: [
{
id: '10', name: 'Project 2', description: 'Project 2 description', products: [
{
id: '100', name: 'Product 1', vendor: 'Vendor 1', instances: [
{ id: '1000', operatingSystem: 'Microsoft Windows 2012R2', environment: 'Prod', version: '4.1', notes: '', },
],
},
{
id: '200', name: 'Product 2', vendor: 'Vendor 2', instances: [
{ id: '2000', operatingSystem: 'Microsoft Windows 2016', environment: 'Prod', version: '4.0', notes: '', },
],
},
],
},
{
id: '20', name: 'Project 1', description: 'Project 1 description', products: [
{
id: '200', name: 'Product 2', vendor: 'Vendor 2', instances: [
{ id: '2000', operatingSystem: 'Microsoft Windows 2016', environment: 'Prod', version: '4.0', notes: '', },
{ id: '3000', operatingSystem: 'RedHat Linux 7', environment: 'Prod', version: '3.12', notes: '', },
],
},
],
},
],
};
const projectToIdMap = source.projects.reduce((projectToIdMap, { name, products }) => {
projectToIdMap[name] = [];
products.forEach(({ instances }) => {
instances.forEach(({ id }) => {
projectToIdMap[name].push(id);
});
});
return projectToIdMap;
}, {});
const products = [
{
id: '100', name: 'Product 1', vendor: 'Vendor 1', instances: [
{ id: '1000', operatingSystem: 'Microsoft Windows 2012R2', environment: 'Prod', version: '4.1', notes: '', },
],
},
{
id: '200', name: 'Product 2', vendor: 'Vendor 2', instances: [
{ id: '2000', operatingSystem: 'Microsoft Windows 2016', environment: 'Prod', version: '4.0', notes: '', },
{ id: '2000', operatingSystem: 'Microsoft Windows 2016', environment: 'Prod', version: '4.0', notes: '', },
{ id: '3000', operatingSystem: 'RedHat Linux 7', environment: 'Prod', version: '3.12', notes: '', },
],
},
];
products.forEach(({ instances }) => {
instances.forEach(instance => {
instance.projects = [];
const { id } = instance;
Object.entries(projectToIdMap).forEach(([project, os], i) => {
if (projectToIdMap[project].includes(id)) {
instance.projects.push({ id: (i + 1) * 10, project });
}
});
});
});
console.log(inspect(products, false, null, true)); // if Node.js
This does not remove the duplicate Microsoft Windows 2016 entry, but I'm sure you can take it from here.
Ok, I think I made it :)
Thanks #GirkovArpa for the assistance!
You can find the source object in the code snippet or in the original question.
The expected object is:
const expected = [
{
id: '100',
name: 'Product 1',
vendor: 'Vendor 1',
instances: [
{
id: '1000',
operatingSystem: 'Microsoft Windows 2012R2',
environment: 'Prod',
version: '4.1',
notes: '',
projects: [
{
id: '10',
name: 'Project 2',
},
],
},
],
},
{
id: '200',
name: 'Product 2',
vendor: 'Vendor 2',
instances: [
{
id: '2000',
operatingSystem: 'Microsoft Windows 2016',
environment: 'Prod',
version: '4.0',
notes: '',
projects: [
{
id: '10',
name: 'Project 2',
},
{
id: '20',
name: 'Project 1',
},
],
},
{
id: '3000',
operatingSystem: 'RedHat Linux 7',
environment: 'Prod',
version: '3.12',
notes: '',
projects: [
{
id: '20',
name: 'Project 1',
},
],
},
],
},
];
First I added each instance its project in the form of an array
Then I flatten the products array results so it will not be split into objects
Then I used #Yevgen Gorbunkov help from the following thread: Merge duplicate items in array of objects, concating nested arrays
I changed his function to also remove duplicate products while merging associated projects (this is why I initialize the project field with an array in the first place)
If you will find a better or more efficient wat to improve the method - I will love to hear :)
EDIT
I changed the code snippet from
dupe.instances = { ...o.instances };
to
dupe.instances = Object.values({ ...o.instances });
for adding the instances as an array instead of an object.
/* Source object */
const source = {
id: '1', name: 'Customer A', projects: [
{
id: '10', name: 'Project 2', description: 'Project 2 description', products: [
{
id: '100', name: 'Product 1', vendor: 'Vendor 1', instances: [
{ id: '1000', operatingSystem: 'Microsoft Windows 2012R2', environment: 'Prod', version: '4.1', notes: '', },
],
},
{
id: '200', name: 'Product 2', vendor: 'Vendor 2', instances: [
{ id: '2000', operatingSystem: 'Microsoft Windows 2016', environment: 'Prod', version: '4.0', notes: '', },
],
},
],
},
{
id: '20', name: 'Project 1', description: 'Project 1 description', products: [
{
id: '200', name: 'Product 2', vendor: 'Vendor 2', instances: [
{ id: '2000', operatingSystem: 'Microsoft Windows 2016', environment: 'Prod', version: '4.0', notes: '', },
{ id: '3000', operatingSystem: 'RedHat Linux 7', environment: 'Prod', version: '3.12', notes: '', },
],
},
],
},
],
};
/* flattenArray function */
function flattenArray(data) {
const initialValue = [];
return data.reduce((total, value) => {
return total.concat(Array.isArray(value) ? flattenArray(value) : value);
}, initialValue);
}
/* Adding an array of project on each instance */
source.projects.forEach(project => {
project.products.forEach(product => {
product.instances.forEach(instance => {
instance.project = [
{
id: project.id,
name: project.name
}
];
});
});
});
/* flatten the array */
let products = flattenArray(source.projects.map(p => p.products));
/* reducing products and merging id equaly instances + projects */
products = [
...products
.reduce((r, o) => {
const dupe = r.get(o.id);
if (dupe) {
const a = flattenArray(dupe.instances).reduce(i => i);
o.instances.forEach(i => {
if (i.id === a.id) {
const merged = [...i.project, ...a.project];
i.project = merged;
}
});
dupe.instances = Object.values({ ...o.instances });
} else {
r.set(o.id, o);
}
return r;
}, new Map())
.values()
];
console.log("products", products);
I have a Sunburst Highcharts in my project. I was wondering if it is possible to change the level without clicking on them.
for example, I have a sunburst like this which has 4 levels.
var data = [{
id: '0.0',
parent: '',
name: 'The World'
}, {
id: '1.3',
parent: '0.0',
name: 'Asia'
}, {
id: '1.1',
parent: '0.0',
name: 'Africa'
}, {
id: '1.2',
parent: '0.0',
name: 'America'
}, {
id: '1.4',
parent: '0.0',
name: 'Europe'
}, {
id: '1.5',
parent: '0.0',
name: 'Oceanic'
},
/* Africa */
{
id: '2.1',
parent: '1.1',
name: 'Eastern Africa'
},
{
id: '3.1',
parent: '2.1',
name: 'Ethiopia',
value: 104957438
}, {
id: '3.2',
parent: '2.1',
name: 'Tanzania',
value: 57310019
}, {
id: '3.3',
parent: '2.1',
name: 'Kenya',
value: 49699862
}, {
id: '3.4',
parent: '2.1',
name: 'Uganda',
value: 42862958
}, {
id: '3.5',
parent: '2.1',
name: 'Mozambique',
value: 29668834
}, {
id: '3.6',
parent: '2.1',
name: 'Madagascar',
value: 25570895
}, {
id: '3.226',
parent: '2.22',
name: 'Samoa',
value: 196440
}, {
id: '3.227',
parent: '2.22',
name: 'Tonga',
value: 108020
}, {
id: '3.228',
parent: '2.22',
name: 'American Samoa',
value: 55641
}, {
id: '3.229',
parent: '2.22',
name: 'Cook Islands',
value: 17380
}, {
id: '3.230',
parent: '2.22',
name: 'Wallis and Futuna',
value: 11773
}, {
id: '3.231',
parent: '2.22',
name: 'Tuvalu',
value: 11192
}, {
id: '3.232',
parent: '2.22',
name: 'Niue',
value: 1618
}, {
id: '3.233',
parent: '2.22',
name: 'Tokelau',
value: 1300
}];
// Splice in transparent for the center circle
Highcharts.getOptions().colors.splice(0, 0, 'transparent');
Highcharts.chart('container', {
chart: {
height: '100%'
},
title: {
text: 'World population 2017'
},
subtitle: {
text: 'Source <href="https://en.wikipedia.org/wiki/List_of_countries_by_population_(United_Nations)">Wikipedia</a>'
},
series: [{
type: "sunburst",
data: data,
allowDrillToNode: true,
cursor: 'pointer',
dataLabels: {
format: '{point.name}',
filter: {
property: 'innerArcLength',
operator: '>',
value: 16
}
},
levels: [{
level: 1,
levelIsConstant: false,
dataLabels: {
filter: {
property: 'outerArcLength',
operator: '>',
value: 64
}
}
}, {
level: 2,
colorByPoint: true
},
{
level: 3,
colorVariation: {
key: 'brightness',
to: -0.5
}
}, {
level: 4,
colorVariation: {
key: 'brightness',
to: 0.5
}
}]
}],
tooltip: {
headerFormat: "",
pointFormat: 'The population of <b>{point.name}</b> is <b>{point.value}</b>'
}
});
Problem is
I want to go to specific levels without clicking on sunburst. for example, I create a button that if the user clicks on it, will do the same action as if I was clicking on Eastern Africa level of my sunburst.
<button onclick="clickOnEasternAfrica()">Do click here</button>
What code should I use for clickOnEasternAfrica() method!?
You can use fireEvent to trigge a click event:
document.getElementById('easternAfrica').addEventListener('click', function(){
var series = chart.series[0];
Highcharts.fireEvent(series, 'click', { point: series.points[6] });
});
Live demo: https://jsfiddle.net/BlackLabel/dLb5hert/
API Reference: https://api.highcharts.com/class-reference/Highcharts#.fireEvent%3CT%3E
I would like to create a drill-down highchart.
You can find the jsfiddle link which is not working but the sample data is in it.
data: [{
name: '6',
y: 14
}, {
name: '7',
y: 19
}, ...
}]
},
{
name: 'B1',
data: [{
name: '6',
y: 14
}, {
name: '7',
y: 19
}, ...
},
{
name: 'C1',
data: [{
name: '6',
y: 14
}, {
name: '7',
y: 19
}, ...
}
]
The vice versa is running here:
datanormal = [{
name: '6',
data: [{
name: 'A1',
y: 14,
drilldown: 'Details1'
}, {
name: 'B1',
y: 19,
drilldown: 'Details1'
}, {
name: 'C1',
y: 21,
drilldown: 'Details1'
}]
},
{
name: '7',
data: [{
name: 'A1',
y: 5,
drilldown: 'Details1'
} ...]
}];
datadrill =
[{
id: 'Details1',
name: 'Details1',
data: [
['D1', 4],
['D2', 2],
['D3', 1],
['D4', 4]
]
}];
I need the opposite, from the basic to complex.
This is the main column chart image
This is the detailed drill-down chart image
If you look fot the working example there is another object for the datadrill:
datadrill =
[{
id: 'Details1',
name: 'Details1',
data: [
['D1', 4],
['D2', 2],
['D3', 1],
['D4', 4]
]
}]
You need to do the same on your code.
Could you check this? Is that what you want?
This is jsfiddle link => https://jsfiddle.net/burakkp/ytkqzfos/2/
$(document).ready(function() {
var datadrill;
datadrill = [{
name: 'A1',
data: [{
name: '6',
y: 14
}, {
name: '7',
y: 19
}, {
name: '8',
y: 21
}, {
name: '9',
y: 34
}, {
name: '10',
y: 5
}, {
name: '11',
y: 9
}]
},
{
name: 'B1',
data: [{
name: '6',
y: 14
}, {
name: '7',
y: 19
}, {
name: '8',
y: 21
}, {
name: '9',
y: 34
}, {
name: '10',
y: 5
}, {
name: '11',
y: 9
}]
},
{
name: 'C1',
data: [{
name: '6',
y: 14
}, {
name: '7',
y: 19
}, {
name: '8',
y: 21
}, {
name: '9',
y: 34
}, {
name: '10',
y: 5
}, {
name: '11',
y: 9
}]
}];
Highcharts.chart('container', {
chart: {
type: 'column'
},
title: {
text: 'Highcharts multi-series drilldown'
},
subtitle: {
text: 'The <em>allowPointDrilldown</em> option makes point clicks drill to the whole category'
},
xAxis: {
type: 'category'
},
plotOptions: {
series: {
borderWidth: 0,
dataLabels: {
enabled: true
}
}
},
series: datadrill
});
});
Would you like to achieve something like this? Please test the drilldown only for the first point (A1).
Demo: https://jsfiddle.net/BlackLabel/wn65fkxj/
I did some changes in your data, like:
datadrill = [{
id: 'Details1',
name: 'A1',
data: [{
name: '6',
y: 14
}, {
name: '7',
y: 19
}, {
name: '8',
y: 21
}, {
name: '9',
y: 34
}, {
name: '10',
y: 5
}, {
name: '11',
y: 9
}]
},
{
id: 'Details2',
name: 'B1',
data: [{
name: '6',
y: 14
}, {
name: '7',
y: 19
}, {
name: '8',
y: 21
}, {
name: '9',
y: 34
}, {
name: '10',
y: 5
}, {
name: '11',
y: 9
}]
},
{
id: 'Details3',
name: 'C1',
data: [{
name: '6',
y: 14
}, {
name: '7',
y: 19
}, {
name: '8',
y: 21
}, {
name: '9',
y: 34
}, {
name: '10',
y: 5
}, {
name: '11',
y: 9
}]
}
];
Also, I added null points to trigger different drilldown to each.
API: https://api.highcharts.com/highcharts/drilldown.allowPointDrilldown
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);