Comparison between two Arrays of Objects failing when item is deleted - javascript

I have the following function that should determine which elements in a nested object were changed, deleted or added:
static modifiedDiff(o1, o2, deep = false, added = [], updated = [], removed = [], path = "", key = "") {
path += key.length > 0 ? key + "." : '';
for (const prop in o1) {
if (o1.hasOwnProperty(prop)) {
const o2PropValue = o2[prop];
const o1PropValue = o1[prop];
if (o2.hasOwnProperty(prop)) {
if (o2PropValue === o1PropValue) {
//unchanged[prop] = o1PropValue;
} else {
if (deep && this.isObject(o1PropValue) && this.isObject(o2PropValue)) {
this.modifiedDiff(o1PropValue, o2PropValue, deep, added, updated, removed, path, this.modifyPropIfNeeded(prop));
} else {
this.addObjectToArray(updated, path + prop, o2PropValue);
}
}
} else {
this.addObjectToArray(removed, path + prop, o1PropValue);
}
}
}
for (const prop in o2) {
if (o2.hasOwnProperty(prop)) {
const o1PropValue = o1[prop];
const o2PropValue = o2[prop];
if (o1.hasOwnProperty(prop)) {
if (o1PropValue !== o2PropValue) {
if (!deep || !this.isObject(o1PropValue)) {
//updated[prop].oldValue = o1PropValue;
}
}
} else {
this.addObjectToArray(added, path + prop, o2PropValue);
}
}
}
return {
added,
updated,
removed
};
}
This works fine for changes, and elements that are added to the second object.
The result looks like this:
{
"added": [
{
"_objects.4": {
"type": "textbox",
"version": "4.1.0",
"originX": "center",
"originY": "center",
"left": 10,
"top": 10,
"width": 84.51,
"height": 45.2,
"fill": "#000000",
"stroke": null,
"strokeWidth": 1,
"strokeDashArray": null,
"strokeLineCap": "butt",
"strokeDashOffset": 0,
"strokeLineJoin": "miter",
"strokeMiterLimit": 4,
"scaleX": 0.5,
"scaleY": 0.5,
"angle": 0,
"flipX": false,
"flipY": false,
"opacity": 1,
"shadow": null,
"visible": true,
"backgroundColor": "",
"fillRule": "nonzero",
"paintFirst": "fill",
"globalCompositeOperation": "source-over",
"skewX": 0,
"skewY": 0,
"text": "hallo",
"fontSize": 40,
"fontWeight": "",
"fontFamily": "helvetica",
"fontStyle": "",
"lineHeight": 1.16,
"underline": false,
"overline": false,
"linethrough": false,
"textAlign": "left",
"textBackgroundColor": "",
"charSpacing": 0,
"minWidth": 20,
"splitByGrapheme": false,
"id": "text_835392",
"styles": {}
}
}
],
"updated": [
{
"_objects.1.left": 372.81
},
{
"_objects.1.top": 179.99
}
],
"removed": []
}
The problem is the following:
If one element in an array of o2 is deleted, every element of that array moves one index forward.
This results in all elements after the deleted element to be moved into the changed or added array.
Example:
modifiedDiff([1,2,3,4], [1,2,4])
should be:
{
"added": [],
"updated": [],
"removed": [
{
"2": 3
}
]
}
but is:
{
"added": [],
"updated": [
{
"2": 4
}
],
"removed": [
{
"3": 4
}
]
}
Any ideas? Thanks for your help!

Related

MXGraph JSON Graphs with Angular - render and read graph using JSON and ANGULAR with mxGraph

I am trying to work with Angular and MX Graphs using JSON, I am sure that many of you also would think it is a great way to serialize graphs and also give them functionality within an angular application.
I have not been able to find a minimum example where a JSON representation of a graph is rendered from Angular.
Inn the demo part of this example with mxgraph using javascript, they simply render the graph from a json structure.
Example:
https://codepen.io/Gatcha/pen/oYexEB
JSON Structure
{
"graph": [
{
"value": {
"name": "Daffy Duck"
},
"geometry": {
"x": 90,
"y": 60,
"width": 120,
"height": 30,
"relative": false,
"TRANSLATE_CONTROL_POINTS": true,
"alternateBounds": null,
"sourcePoint": null,
"targetPoint": null,
"points": null,
"offset": null
},
"id": "2",
"vertex": true,
"connectable": true,
"parent": "1",
"source": null,
"target": null,
"edges": [
{
"value": "Edge",
"geometry": {
"x": 0,
"y": 0,
"width": 0,
"height": 0,
"relative": true
},
"id": "4",
"edge": true,
"parent": "1",
"source": "2",
"target": "3",
"mxObjectId": "mxCell#7"
}
],
"mxObjectId": "mxCell#6"
},
{
"value": {
"name": "Bugs Bunny"
},
"geometry": {
"x": 1020,
"y": 60,
"width": 120,
"height": 30,
"relative": false,
"TRANSLATE_CONTROL_POINTS": true,
"alternateBounds": null,
"sourcePoint": null,
"targetPoint": null,
"points": null,
"offset": null
},
"id": "3",
"vertex": true,
"connectable": true,
"parent": "1",
"source": null,
"target": null,
"edges": [
{
"value": "Edge",
"geometry": {
"x": 0,
"y": 0,
"width": 0,
"height": 0,
"relative": true
},
"id": "4",
"edge": true,
"parent": "1",
"source": "2",
"target": "3",
"mxObjectId": "mxCell#7"
}
],
"mxObjectId": "mxCell#8"
},
{
"value": "Edge",
"geometry": {
"x": 0,
"y": 0,
"width": 0,
"height": 0,
"relative": true
},
"id": "4",
"edge": true,
"parent": "1",
"source": "2",
"target": "3",
"mxObjectId": "mxCell#7"
},
{
"value": {
"name": "Elmer Fudd"
},
"geometry": {
"x": 90,
"y": 220,
"width": 120,
"height": 30,
"relative": false,
"TRANSLATE_CONTROL_POINTS": true,
"alternateBounds": null,
"sourcePoint": null,
"targetPoint": null,
"points": null,
"offset": null
},
"id": "5",
"vertex": true,
"connectable": true,
"parent": "1",
"source": null,
"target": null,
"edges": [
{
"value": "Edge",
"geometry": {
"x": 0,
"y": 0,
"width": 0,
"height": 0,
"relative": true
},
"id": "7",
"edge": true,
"parent": "1",
"source": "5",
"target": "6",
"mxObjectId": "mxCell#10"
}
],
"mxObjectId": "mxCell#9"
},
{
"value": {
"name": "Tasmanian Devil"
},
"geometry": {
"x": 1020,
"y": 220,
"width": 120,
"height": 30,
"relative": false,
"TRANSLATE_CONTROL_POINTS": true,
"alternateBounds": null,
"sourcePoint": null,
"targetPoint": null,
"points": null,
"offset": null
},
"id": "6",
"vertex": true,
"connectable": true,
"parent": "1",
"source": null,
"target": null,
"edges": [
{
"value": "Edge",
"geometry": {
"x": 0,
"y": 0,
"width": 0,
"height": 0,
"relative": true
},
"id": "7",
"edge": true,
"parent": "1",
"source": "5",
"target": "6",
"mxObjectId": "mxCell#10"
}
],
"mxObjectId": "mxCell#11"
},
{
"value": "Edge",
"geometry": {
"x": 0,
"y": 0,
"width": 0,
"height": 0,
"relative": true
},
"id": "7",
"edge": true,
"parent": "1",
"source": "5",
"target": "6",
"mxObjectId": "mxCell#10"
}
]
}
and this is the Javascript code that renders the graph, where the json is obtained from a text area called "from" and pased to JSON, pretty basic and simple:
/******************************************************************
Demo
******************************************************************/
const graphX = new GraphX(document.getElementById('graphContainer'));
document.getElementById('buttons').appendChild(mxUtils.button('From JSON', () => {
const dataModel = JSON.parse(document.getElementById('from').innerHTML);
graphX.render(dataModel);
}));
document.getElementById('buttons').appendChild(mxUtils.button('To JSON', () => {
const jsonNodes = graphX.getJsonModel();
document.getElementById('to').innerHTML = `<pre>${syntaxHighlight(stringifyWithoutCircular(jsonNodes))}</pre>`;
}));
Does anyone have a little Idea of how to do this with Angular, in any way in order to start documenting this for everyone also?
Thanks a lot.
May the force be with you.
Note: There is the mxHierarchicalLayout.js class that i think would allow you to create a hierarchical model of the graph.
Here is the whole javascript file.
I will keep trying and post whatever I learn, but please if you have a hint, would be really apretiated.
/******************************************************************
Core
******************************************************************/
class JsonCodec extends mxObjectCodec {
constructor() {
super((value)=>{});
}
encode(value) {
const xmlDoc = mxUtils.createXmlDocument();
const newObject = xmlDoc.createElement("Object");
for(let prop in value) {
newObject.setAttribute(prop, value[prop]);
}
return newObject;
}
decode(model) {
return Object.keys(model.cells).map(
(iCell)=>{
const currentCell = model.getCell(iCell);
return (currentCell.value !== undefined)? currentCell : null;
}
).filter((item)=> (item !== null));
}
}
class GraphX {
constructor(container){
if (!mxClient.isBrowserSupported()) {
return mxUtils.error('Browser is not supported!', 200, false);
}
mxEvent.disableContextMenu(container);
this._graph = new mxGraph(container);
this._graph.setConnectable(true);
this._graph.setAllowDanglingEdges(false);
new mxRubberband(this._graph); // Enables rubberband selection
this.labelDisplayOveride();
this.styling();
}
labelDisplayOveride() { // Overrides method to provide a cell label in the display
this._graph.convertValueToString = (cell)=> {
if (mxUtils.isNode(cell.value)) {
if (cell.value.nodeName.toLowerCase() === 'object') {
const name = cell.getAttribute('name', '');
return name;
}
}
return '';
};
}
styling() {
// Creates the default style for vertices
let style = [];
style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_RECTANGLE;
style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter;
style[mxConstants.STYLE_STROKECOLOR] = 'gray';
style[mxConstants.STYLE_ROUNDED] = true;
style[mxConstants.STYLE_FILLCOLOR] = '#EEEEEE';
style[mxConstants.STYLE_GRADIENTCOLOR] = 'white';
style[mxConstants.STYLE_FONTCOLOR] = 'black';
style[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_CENTER;
style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_MIDDLE;
style[mxConstants.STYLE_FONTSIZE] = '12';
style[mxConstants.STYLE_FONTSTYLE] = 1;
this._graph.getStylesheet().putDefaultVertexStyle(style);
// Creates the default style for edges
style = this._graph.getStylesheet().getDefaultEdgeStyle();
style[mxConstants.STYLE_STROKECOLOR] = '#0C0C0C';
style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR] = 'white';
style[mxConstants.STYLE_EDGE] = mxEdgeStyle.ElbowConnector;
style[mxConstants.STYLE_ROUNDED] = true;
style[mxConstants.STYLE_FONTCOLOR] = 'black';
style[mxConstants.STYLE_FONTSIZE] = '10';
this._graph.getStylesheet().putDefaultEdgeStyle(style);
}
getJsonModel(){
const encoder = new JsonCodec();
const jsonModel = encoder.decode(this._graph.getModel());
return {
"graph": jsonModel
}
}
render(dataModel) {
const jsonEncoder = new JsonCodec();
this._vertices = {};
this._dataModel = dataModel;
const parent = this._graph.getDefaultParent();
this._graph.getModel().beginUpdate(); // Adds cells to the model in a single step
try {
this._dataModel.graph.map(
(node)=> {
if(node.value) {
if(typeof node.value === "object") {
const xmlNode = jsonEncoder.encode(node.value);
this._vertices[node.id] = this._graph.insertVertex(parent, null, xmlNode, node.geometry.x, node.geometry.y, node.geometry.width, node.geometry.height);
} else if(node.value === "Edge") {
this._graph.insertEdge(parent, null, 'Edge', this._vertices[node.source], this._vertices[node.target])
}
}
}
);
} finally {
this._graph.getModel().endUpdate(); // Updates the display
}
}
}
/******************************************************************
Demo
******************************************************************/
const graphX = new GraphX(document.getElementById('graphContainer'));
document.getElementById('buttons').appendChild(mxUtils.button('From JSON', () => {
const dataModel = JSON.parse(document.getElementById('from').innerHTML);
graphX.render(dataModel);
}));
document.getElementById('buttons').appendChild(mxUtils.button('To JSON', () => {
const jsonNodes = graphX.getJsonModel();
document.getElementById('to').innerHTML = `<pre>${syntaxHighlight(stringifyWithoutCircular(jsonNodes))}</pre>`;
}));
/******************************************
Utils
********************************************/
function stringifyWithoutCircular(json){
return JSON.stringify(
json,
( key, value) => {
if((key === 'parent' || key == 'source' || key == 'target') && value !== null) {
return value.id;
} else if(key === 'value' && value !== null && value.localName) {
let results = {};
Object.keys(value.attributes).forEach(
(attrKey)=>{
const attribute = value.attributes[attrKey];
results[attribute.nodeName] = attribute.nodeValue;
}
)
return results;
}
return value;
},
4
);
}
function syntaxHighlight(json) {
json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
var cls = 'number';
if (/^"/.test(match)) {
if (/:$/.test(match)) {
cls = 'key';
} else {
cls = 'string';
}
} else if (/true|false/.test(match)) {
cls = 'boolean';
} else if (/null/.test(match)) {
cls = 'null';
}
return '<span class="' + cls + '">' + match + '</span>';
});
}
I was able to print the XML tree.
Here is how.
After you have a graph created in your typescript, there is an encoder class that you can use to get the XML representation of your basic graph. This serves as a basic step to start playing with the XML version of graphs and be able to render and serialize graphs.
var encoder=new mxCodec();
var node = encoder.encode(graph.getModel());
var graphText=mxUtils.getXml(node);
console.log(graphText)

Compare 3 arrray and find combination in javascript

I have 3 arrays as below
A: [
{
"label": "100Watts",
"value": "100Watts",
},
{
"label": "135Watts",
"value": "135Watts",
} ]
B: [
{
"label": "Large|100cm",
"value": "Large|100cm"
},
{
"label": "Small|125mm",
"value": "Small|125mm"
}
]
C: [
{
"label": "Black",
"value": "Black",
},
{
"label": "Black",
"value": "Black",
}
]
I have one master array as below
result:[
{
"x_powerConsumption": "100Watts",
"x_size": "Small|125mm",
"x_color": "Black",
},
{
"x_powerConsumption": "135Watts",
"x_size": "Large|100cm",
"x_color": "Black",
}
]
My code as below
let newArray = A.filter(o1 => result.some(o2 => o1.label === o2.x_size));
let newArray2 = B.filter(o1 => result.some(o2 => o1.label === o2.x_powerConsumption));
this.setState({selectedVarientOne: newArray[0].label, selectedVarientTwo:newArray2[0].label})
But my requirement is i need combination as below like
Output : Black --> 100Watts --> Small|125mm
But as per my above code it is coming like
Black --> 100Watts --> Large|100mm
Can anyone tell me how i can solve this?
Ok, I'm assuming that you want to match the array of objects to the main object
I just use a nest of for loops to see if values from an array(A,B, or C) match up with values from the main object(result) from only the important keys in the result object of course
var A=[{"label":"100Watts","value":"100Watts"},{"label":"135Watts","value":"135Watts"}]
var B=[{"label":"Large|100cm","value":"Large|100cm"},{"label":"Small|125mm","value":"Small|125mm"}]
//now for function to correlate data(sry for late answer was afk lol)
function attribute(arr,result){
const necessary_keys=["x_powerConsumption","x_size","x_color"]
var toReturn=[] //array with output to return
for(let i=0;i<arr.length;i++){
var arrValues=Object.values(arr[i]) //eg: A[0] would be ["100Watts","100Watts"]
for(let j=0;j<result.length;j++){
var resultValues=Object.keys(result[j])
.filter(key=>necessary_keys.includes(key))
.map(key=>result[j][key]) //result[0] would be ["100Watts","Small|125mm","Black"]
if(resultValues.some(value=>arrValues.includes(value))){
toReturn[i]=resultValues //["100Watts","Small|125mm","Black"] for arr[0]
}
}
}
return toReturn //all indexes with undefined dont correlate with result in any of its values
}
//now to use the function
let newArray=attribute(A,result)
let newArray2=attribute(B,result)
console.log("values from A:",newArray)
console.log("values from B:",newArray2)
<script>
var result=[
{
"dynamicPropertyMapLong": {
"sku-Equipment_x_color": 1,
"sku-Equipment_x_size": 1,
"sku-Equipment_x_powerConsumption": 3
},
"x_UAE_installationPrice": 0,
"bundleLinks": [],
"largeImage": null,
"smallImage": null,
"listVolumePrice": null,
"onlineOnly": false,
"listPrices": {
"aed": 100,
"loyaltyProgram": null
},
"configurationMetadata": [],
"largeImageURLs": [],
"x_skuCreationDate": null,
"productLine": null,
"listVolumePrices": {
"aed": null,
"loyaltyProgram": null
},
"derivedSalePriceFrom": "aed",
"model": null,
"x_powerConsumption": "100Watts",
"barcode": null,
"salePriceEndDate": null,
"images": [],
"unitOfMeasure": null,
"primaryMediumImageURL": null,
"dynamicPropertyMapBigString": {},
"active": true,
"x_promotionDetails": "5 Percent Off: 5OFF5",
"thumbImageURLs": [],
"mediumImageURLs": [],
"primarySourceImageURL": null,
"x_description": null,
"sourceImageURLs": [],
"primarySmallImageURL": null,
"x_autoShipPrice": 0,
"productFamily": null,
"primaryThumbImageURL": null,
"nonreturnable": false,
"x_loyaltyLevel": "LEVEL2",
"displayName": "Three variant sku1",
"salePrices": {
"aed": null,
"loyaltyProgram": null
},
"primaryFullImageURL": null,
"productListingSku": null,
"primaryLargeImageURL": null,
"derivedOnlineOnly": false,
"smallImageURLs": [],
"thumbnailImage": null,
"saleVolumePrices": {
"aed": null,
"loyaltyProgram": null
},
"x_size": "Small|125mm",
"saleVolumePrice": null,
"salePriceStartDate": null,
"quantity": null,
"salePrice": null,
"fullImageURLs": [],
"x_isonSale": "Y",
"variantValuesOrder": {},
"x_color": "Black",
"soldAsPackage": true,
"listingSKUId": null,
"x_SAR_installationPrice": 0,
"repositoryId": "300003-1",
"derivedListPriceFrom": "aed",
"x_installationPrice": 0,
"x_costPrice": null,
"configurable": false,
"listPrice": 100
},
{
"dynamicPropertyMapLong": {
"sku-Equipment_x_color": 1,
"sku-Equipment_x_size": 2,
"sku-Equipment_x_powerConsumption": 1
},
"x_UAE_installationPrice": 0,
"bundleLinks": [],
"largeImage": null,
"smallImage": null,
"listVolumePrice": null,
"onlineOnly": false,
"listPrices": {
"aed": 135,
"loyaltyProgram": null
},
"configurationMetadata": [],
"largeImageURLs": [],
"x_skuCreationDate": null,
"productLine": null,
"listVolumePrices": {
"aed": null,
"loyaltyProgram": null
},
"derivedSalePriceFrom": "aed",
"model": null,
"x_powerConsumption": "135Watts",
"barcode": null,
"salePriceEndDate": null,
"images": [],
"unitOfMeasure": null,
"primaryMediumImageURL": null,
"dynamicPropertyMapBigString": {},
"active": true,
"x_promotionDetails": "5 Percent Off: 5OFF5",
"thumbImageURLs": [],
"mediumImageURLs": [],
"primarySourceImageURL": null,
"x_description": null,
"sourceImageURLs": [],
"primarySmallImageURL": null,
"x_autoShipPrice": 0,
"productFamily": null,
"primaryThumbImageURL": null,
"nonreturnable": false,
"x_loyaltyLevel": "LEVEL2",
"displayName": "sku3",
"salePrices": {
"aed": null,
"loyaltyProgram": null
},
"primaryFullImageURL": null,
"productListingSku": null,
"primaryLargeImageURL": null,
"derivedOnlineOnly": false,
"smallImageURLs": [],
"thumbnailImage": null,
"saleVolumePrices": {
"aed": null,
"loyaltyProgram": null
},
"x_size": "Large|100cm",
"saleVolumePrice": null,
"salePriceStartDate": null,
"quantity": null,
"salePrice": null,
"fullImageURLs": [],
"x_isonSale": "Y",
"variantValuesOrder": {},
"x_color": "Black",
"soldAsPackage": true,
"listingSKUId": null,
"x_SAR_installationPrice": 0,
"repositoryId": "300003-3",
"derivedListPriceFrom": "aed",
"x_installationPrice": 0,
"x_costPrice": null,
"configurable": false,
"listPrice": 135
}
]
</script>
Assuming the other answer is correct, here is a shorter version:
result.map(({x_powerConsumption, x_size, x_color}) => ([
A.find(el => el.label === x_powerConsumption).value,
B.find(el => el.label === x_size).value,
C.find(el => el.label === x_color).value
]))
Example:
const A = [{ "label": "100Watts", "value": "100Watts" }, { "label": "135Watts", "value": "135Watts" }];
const B = [{ "label": "Large|100cm", "value": "Large|100cm" }, { "label": "Small|125mm", "value": "Small|125mm" }];
const C = [{ "label": "Black", "value": "Black" }, { "label": "Black", "value": "Black" }];
const result = [
{ "x_powerConsumption": "100Watts", "x_size": "Small|125mm", "x_color": "Black" },
{ "x_powerConsumption": "135Watts", "x_size": "Large|100cm", "x_color": "Black" }
];
console.log(result.map(({x_powerConsumption, x_size, x_color}) => ([
A.find(el => el.label === x_powerConsumption).value,
B.find(el => el.label === x_size).value,
C.find(el => el.label === x_color).value
])));

filtering item that has nested objects based on condition and returning the item

I have a status list array from an api & i want to filter out the service that has stopped, so if 'online' I want to return the item in console or state. how can I achieve this? can someone tell me what i'm doing wrong. here is my code -
const statusList = [
{
"pid": 0,
"name": "dailyScripts.job",
"pm2_env": {
"namespace": "default",
"kill_retry_time": 100,
"windowsHide": true,
"username": "u4011",
"treekill": true,
"status": "stopped"
},
"pm_id": 0,
"monit": {
"memory": 0,
"cpu": 0
}
},
{
"pid": 1,
"name": "finn.job",
"pm2_env": {
"namespace": "default",
"kill_retry_time": 100,
"windowsHide": true,
"username": "u3411",
"treekill": true,
"status": "online"
},
"pm_id": 1,
"monit": {
"memory": 0,
"cpu": 1
}
}
]
const data = statusList.filter(service => Object.keys(service.pm2_env.status) === 'online');
console.log(data, 'data');
The Object.keys() call is not necessary here, without it, the filter() result should be as you expect:
const statusList = [ { "pid": 0, "name": "dailyScripts.job", "pm2_env": { "namespace": "default", "kill_retry_time": 100, "windowsHide": true, "username": "u4011", "treekill": true, "status": "stopped" }, "pm_id": 0, "monit": { "memory": 0, "cpu": 0 } }, { "pid": 1, "name": "finn.job", "pm2_env": { "namespace": "default", "kill_retry_time": 100, "windowsHide": true, "username": "u3411", "treekill": true, "status": "online" }, "pm_id": 1, "monit": { "memory": 0, "cpu": 1 } } ]
const data = statusList.filter(service => service.pm2_env.status === 'online');
console.log(data, 'data');
I'm not sure why you are using Object.keys, but it isn't needed here
From MDN:
The Object.keys() method returns an array of a given object's own enumerable property names, iterated in the same order that a normal loop would.
So the solution becomes:
const data = statusList.filter(service => service.pm2_env.status === 'online');

Problem accessing array using Higcharts, how may i access to x position?

I'm trying to access an array position passed by UserOptions using Highcharts.
Series declaration, where i declare the array
Implementation
The problem is that, when i try to access an element of this array it is not shown.
Just see:
Accessing array
However, when i access it directly and print it using console.log() this value is shown.
console.log(a.series[1].userOptions.cuotasPendientes[5]);
Accessing directly
Thnx guys.
The code:
var a = Highcharts.chart('grafico-CIPrestamos-161107279383',{
"chart": {
"type": "column",
"style": {
"fontFamily": "Arial, Tahoma, Sans-serif",
"fontSize": "11px"
}
},
"credits": {
"enabled": false
},
"legend": {
"enabled": true,
"useHTML": true,
"itemMarginTop": 5,
"labelFormatter": function() {
return '<div class="highchartsCustom-legend-label">' + this.name + '</div>';
}
},
"title": {
"text": ""
},
"xAxis": {
"type": "datetime",
"dateTimeLabelFormats": {
"month": "%b"
},
"title": "",
"units": [
["month", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]]
],
"tickInterval": 1,
"labels": {
"useHTML": true,
"format": "<span class=\"highchartsCustom-xAxis-label\">{value:%b}</span>",
"rotation": 0
},
"plotLines": [{
"color": "#ccc",
"width": 1,
"value": 1525132800000,
"dashStyle": "Dash",
"label": {
"useHTML": true,
"text": "<span class=\"highchartsCustom-xAxis-plotLine-label\">2018</span>",
"rotation": 90
}
}, {
"color": "#ccc",
"width": 1,
"value": 1546300800000,
"dashStyle": "Dash",
"label": {
"useHTML": true,
"text": "<span class=\"highchartsCustom-xAxis-plotLine-label\">2019</span>",
"rotation": 90
}
}]
},
"yAxis": {
"allowDecimals": false,
"min": 0,
"title": "",
"labels": {
"useHTML": true,
"format": "<span class=\"highchartsCustom-yAxis-label\">{value:,.0f}</span>"
},
"stackLabels": {
"enabled": false
}
},
"tooltip": {
"useHTML": true,
"headerFormat": "<table class=\"highchartsCustom-tooltip\">",
"pointFormat": "<tr><td class=\"highchartsCustom-tooltip-fecha\">{point.x:%B %Y}</td></tr><tr><td class=\"highchartsCustom-tooltip-linkedParent\">{point.series.linkedParent.name}</td></tr><tr><td class=\"highchartsCustom-tooltip-serie\">FIELD:{point.series.userOptions.cuotasPendientes[3]}</td></tr><tr><td class=\"highchartsCustom-tooltip-serie\">{point.series.name}: <span>{point.y}</span></td></tr><tr><td class=\"highchartsCustom-tooltip-total\">Total: <span>{point.stackTotal:,.0f}</span></td></tr>",
"footerFormat": "</table>",
"style": {
"padding": "1px"
}
},
"plotOptions": {
"series": {
"pointStart": 1525132800000,
"pointIntervalUnit": "month",
"pointWidth": 20,
"events": {
"legendItemClick": function(event) {
var cantidadSeleccionables = event.target.chart.series.length - 1;
if (event.target.visible && cantidadSeleccionables > contadorCIPrestamos) {
contadorCIPrestamos += 1;
return true;
}
if (!event.target.visible) {
contadorCIPrestamos -= 1;
return true;
}
return false;
}
}
},
"column": {
"stacking": "normal",
"dataLabels": {
"enabled": false
}
}
},
"series": [{
"name": "BCO 1",
"id": "BCO1",
"linkedTo": null,
"data": null,
"color": "#B07CD8",
"CuotasPendientes": null,
"vigente": "La bco es vighente",
"Estado": null,
"CapitalOriginal": 0.0,
"TotalCuotas": 0
}, {
"name": "PP1",
"id": null,
"linkedTo": "BCO1",
"data": [22.0, 23.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
"color": "#B07CD8",
"cuotasPendientes": [0, 0, 223, 223, 223, 223, 223, 223, 223, 223, 223],
"vigente": "La serie es vigente",
"Estado": null,
"CapitalOriginal": 0.0,
"TotalCuotas": 0
}],
"colors": ["#B07CD8"]})
console.log(a.series[1].userOptions.cuotasPendientes[5]);
I tried using the formatther and it works, also i doscovered the way to access too.
Instead of using
point.series.cuotasPendientes[2], we have to use point.series.cuotasPendientes.2
Thanks for all.
u can call it directely or loop through CuotasPendientes ur value this way and dont forget to check CuotasPendientes value if its null the loop will not process (i already added it the null check).
console.log(a.series[1].CuotasPendientes[0])
for(let i = 0 ; i < a.series.length; i++){
if (a.series[i].CuotasPendientes != null) {
for (x of a.series[i].CuotasPendientes) {
console.log(x)
}
}
}
Hope its help

How to check if all numbers in an object are within a certain range?

One of the features I'd like to include in my current JS/jQuery project is to allow users to export and import their saved data. The import/export data is just one big object being ran through JSON.stringify and JSON.parse, and implementing a basic version of that has worked well, but I'd like to validate the imported data to make sure it will work(this is less about working as JavaScript and more about working within the rules of the game I'm making).
I'd like to come up with a more efficient script to validate this big complicated object. The only parts of the object I'm concerned with are the numbers, but it also includes strings, arrays, nested objects. Some of the nested objects include numbers I'm concerned with, but also more other junk including even further nested objects. Currently, I'm using jQuery's each() function to loop over each subsection of the object and evaluate it, but this requires me to write an each function for each subsection I'm concerned with.
How can I feed a function this whole object and have it look at just the numbers to see if they're greater than or less than a set value?
Edit: Pasting the object here, the goal is to check if all of the numbers. Some need to be between 0 and 3, some need to be between 0 and 9.
{
"meta": {
"id": 2,
"name": "Test",
"concept": "test description"
},
"coreAttributes": {
"Strength": 3,
"Finesse": 1,
"Perception": 9,
"Stamina": 1,
"Agility": 1,
"Wits": 1,
"Willpower": 1,
"Charisma": 1,
"Intelligence": 1
},
"skills": {
"Toughness": ["Strength", "STR", 0],
"Stealth": ["Finesse", "FIN", 0],
"Alertness": ["Perception", "PER", 3],
"Investigate": ["Perception", "PER", 0],
"Athletics": ["Agility", "AGI", 0],
"Drive": ["Wits", "WIT", 0],
"Survival": ["Wits", "WIT", 0],
"Guts": ["Willpower", "WIL", 4],
"Hardware": ["Intelligence", "INT", 0],
"Humanities": ["Intelligence", "INT", 0],
"Medicine": ["Intelligence", "INT", 0],
"Science": ["Intelligence", "INT", 0],
"Software": ["Intelligence", "INT", 0],
"Charm": ["Charisma", "CHA", 0],
"Manipulate": ["Charisma", "CHA", 0]
},
"unrolledSkills": {
"Contacts": 0,
"Languages": 0,
"Resources": 0
},
"combatSkills": {
"Unarmed": ["Strength", "STR", 0],
"Defense": ["Finesse", "FIN", 0],
"Melee": ["Finesse", "FIN", 0],
"Firearms": ["Perception", "PER", 0],
"Ballistics": ["Perception", "PER", 0],
"Initiative": ["Wits", "WIT", 0]
},
"attacks": {},
"status": {
"currentEndurance": 4,
"currentSpeed": 4,
"currentEntanglement": 4,
"body": {
"upper": {
"wounds": 0,
"armor": ["", 0]
},
"lower": {
"wounds": 0,
"armor": ["", 0]
},
"main": {
"wounds": 0,
"armor": ["", 0]
},
"off": {
"wounds": 0,
"armor": ["", 0]
},
"legs": {
"wounds": 0,
"armor": ["", 0]
}
}
},
"styles": {
"classes": {
"Strength": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Finesse": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Perception": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Stamina": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Agility": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Wits": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Willpower": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Charisma": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Intelligence": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
}
},
"arcane": {
"restoration": 0,
"evocation": 0,
"abjuration": 0,
"sublimation": 0,
"paradigm": 0,
"telepathy": 0,
"shift": 0,
"electromagnetism": 0,
"gravitonertia": 0,
"chromodynamism": 0,
"technology": 0
},
"extension": {
"avatar": 0,
"proxy": 0,
"permanence": 0
}
},
"addenda": {}
}
The below uses recursion to search all keys in a multidimensional object for any numbers lower or greater than the supplied numbers.
Simply call the function like checkObject(myStuff, 30, 60); passing in your object, your lowest allowed number, and your highest allowed number
var myStuff = { "results": [
{
"ACL": {
"7UeILO5tC4": {
"count": "45",
"read": true
},
"role:Leads": {
"count": "12",
"read": true,
"write": true
}
},
"createdAt": "2014-12-16T22:04:46.338Z",
"finishDate": "12%2F16%2F2014",
"finishTime": "16%3A4%3A44",
"objectId": "tVldoxxdCB",
"passFail": "Pass",
"passingPercentage": "56",
"passingPoints": "34",
"questions": "21",
"quizName": "Name",
"quizType": "Flights",
"teamMember": "Jame Fellows",
"ttlPossiblePoints": "59",
"updatedAt": "2014-12-16T22:04:46.338Z",
"userName": "Jame.Fellows",
"userPercentage": "95",
"userPoints": "20",
"userRightAnswers": "57"
},
{
"ACL": {
"7UeILO5tC4": {
"count": "44",
"read": true
},
"role:Leads": {
"count": "12",
"read": true,
"write": true
}
},
"createdAt": "2014-12-16T22:04:46.338Z",
"finishDate": "12%2F16%2F2014",
"finishTime": "16%3A4%3A44",
"objectId": "tVldoxxdCB",
"passFail": "Pass",
"passingPercentage": "90",
"passingPoints": "87",
"questions": "21",
"quizName": "Name",
"quizType": "Flights",
"teamMember": "Jame Fellows",
"ttlPossiblePoints": "79",
"updatedAt": "2014-12-16T22:04:46.338Z",
"userName": "Jame.Fellows",
"userPercentage": "76",
"userPoints": "20",
"userRightAnswers": "45"
},
{
"ACL": {
"7UeILO5tC4": {
"count": "45",
"read": true
},
"role:Leads": {
"count": "12",
"read": true,
"write": true
}
},
"createdAt": "2014-12-16T22:04:46.338Z",
"finishDate": "12%2F16%2F2014",
"finishTime": "16%3A4%3A44",
"objectId": "tVldoxxdCB",
"passFail": "Pass",
"passingPercentage": "90",
"passingPoints": "19",
"questions": "21",
"quizName": "Name",
"quizType": "Flights",
"teamMember": "Jame Fellows",
"ttlPossiblePoints": "21",
"updatedAt": "2014-12-16T22:04:46.338Z",
"userName": "Jame.Fellows",
"userPercentage": "95",
"userPoints": "20",
"userRightAnswers": "20"
}
] };
// track how many invalid numbers we find
var hasInvalidData=0;
// call our checkObject() function, pass it
// your object, your lowest allowed number, your highest allowed number
checkObject(myStuff, 30, 60);
if(hasInvalidData > 0){
alert(hasInvalidData + ' invalid numbers were found')
}
function checkObject(object, low, high){
// loop through each property of the object
for (var property in object) {
// make sure it's a real property and not inherited
if (object.hasOwnProperty(property)) {
//get the value of the current property
var value = object[property];
// if this propery is itself an object,
// call this function recursively
if(typeof object[property] == "object" && typeof object[property] !== null){
checkObject(object[property], low, high)
}
else{
// if it's not an object
// check if it a a number and not true or false
// which wihich isNaN sees as 1 and 0
if( !isNaN(value) && typeof value != "boolean"){
console.log(value);
if(value < low){
console.log('^ this value is too small ^');
hasInvalidData++;
}
if(value > high){
console.log('^ this value is too large ^');
hasInvalidData++;
}
}
}
}
}
}
What you're looking for is actually very simple – recursion.
The below code checks if all numbers are larger than or equal to 2 and will return false on first non-matching number.
var x = {
b: 5,
c: {
d: 3,
e: [1, 2, 3],
f: function() {}
},
g: function() {}
};
var recurse = function(a) {
var s = true;
for (prop in a) {
switch (typeof a[prop]) {
case "number":
//Check for conditions here
console.log("found number " + a[prop]);
if (a[prop] < 2) {
return false;
}
break;
case "object":
if (a[prop] !== null) {
s = s && recurse(a[prop]);
}
break;
default:
break;
}
}
return s;
}
recurse(x);
Here is an iterative solution:
var x = {
b: 5,
c: {
d: 3,
e: [1, 2, 3],
f: function() {}
},
g: function() {}
};
function check (obj) {
var queue = [obj];
while (queue.length > 0) {
var current = queue.shift();
for (var prop in current) {
if (!current.hasOwnProperty(prop)) continue;
switch (typeof(current[prop])) {
case "number":
// check for conditions here
if (current[prop] < 2) {
return false;
}
break;
case "object":
if (current[prop] !== null) {
queue.push(current[prop]);
}
break;
default:
break;
}
}
}
return true;
}
alert(check(x));
It's a bit faster since it can finish early
Here you go, you only need this: (it also has some fancy visualisation :P)
function traverse(obj, callback, level) {
for (var i in obj) {
if (!obj.hasOwnProperty(i)) continue;
callback.apply(this, [i, obj[i], level||""]);
(obj[i] !== null && typeof(obj[i])=="object") && traverse(obj[i],callback,(level||"")+" ");
}
}
// it is called like this
traverse(obj, valueCheck);
//obj - is your object as in your question
//valueCheck(key, val, level) - is your own function to check the value
An exemplary valueCheck() function might be as follows
function valueCheck(key, val, level) {
// proceed to check only if the value is a number
if (!isNaN(parseFloat(val)) && isFinite(val)) {
var isError = ! ((0 <= val) && (val <= 9));
console.log(level + key + " : " + val + (isError && " ----------------------- VALUE ERROR" || ""));
} else {
// if it's a new object, print shiny "-" at the begining
(typeof val == "object") && console.log(Array(1+level.length).join("-") + key);
}
}

Categories