# Problem
Hello. I have a JSON response containing a varying amount of objects (a set of indicators), each containing a fixed set of other objects (geometries) that each contain properties (one of which is 'score').
I'm trying to gather these 'score' properties in order to later do stuff such as min/mean/max by geometry.
# Sample
Here's an example (keeping in mind there could be more than two indicators):
let data = [ {
{
"indicator": "A",
"geom": "1",
"score": 1
},
{
"indicator": "A",
"geom": "2",
"score": 2
} }, {
{
"indicator": "B",
"geom": "1",
"score": 3
},
{
"indicator": "B",
"geom": "2",
"score": 4
} } ]
# Expected result
The result I'm looking for would be something like this, with concatenated values originating from different sub-objects :
let expectedResult = {
{
"indicator": ["A", "B"],
"geom": "1",
"score": [1,3]
},
{
"indicator": ["A", "B],
"geom": "2",
"score": [2,4]
} }
# My (no good) solution
My current, ugly buggy solution is to create an array with all geom ids :
let id = data[0].map(obj => obj.geom);
Then get a complete list of all key-value :
let keyval;
data.map((indic) => { indic.map((geom) =>
{ keyval.push([car.geom, car.score])})});
And finally combine geom id var with values that have identical id (and slice off the redundant id) :
id.map((geom, idx) => {keyval.map((arr) => {
if (car === arr[0]) { id.push(geom, arr.splice(0,1)})
}
})
});
Would anyone know of a more elegant/efficient.. and more importantly working solution ? During my research saw a lot of Array.prototype.reduce(), but didn't figure out how to use it in such a nested configuration.
Thanks,
O.
Use Array#reduce to collect the values into a Map, then use Map#values, and the spread syntax to convert back to an array:
const data = [[{"indicator":"A","geom":"1","score":1},{"indicator":"A","geom":"2","score":2}],[{"indicator":"B","geom":"1","score":3},{"indicator":"B","geom":"2","score":4}]];
const result = [...[].concat(...data).reduce((map, o) => {
const item = map.get(o.geom) || { geom: o.geom, indicator: [], score: [] }; // get the item from the map, or create a new one
item.indicator.push(o.indicator);
item.score.push(o.score);
return map.set(o.geom, item); // set the item and return the map reference
}, new Map).values()]; // get the map values iterator, and use spread (...) to get an array
console.log(result);
Related
I have a nested object. What I am doing is appending a new object to the main object then deleting one of the nested objects and it works fine. What I am trying to do is once I delete one of the nested objects I want to sort the rest by ascending order but also rename the keys to be consecutive. To explain that better say once I remove a nested object my main object is this { 0: {}, 1: {}, 3: {}} meaning nested object key 2 has been removed, now what I want is to change the keys to be { 0: {}, 1: {}, 2: {}} so that they are ascending and consecutive. Thanks in advance.
var myObject = {
0: {
"category": "myCategory1",
"title": "myTitle1"
},
1: {
"category": "myCategory2",
"title": "myTitle2"
}
}
const currentObjectKeys = Object.keys(myObject).sort();
const nextObjectKey = parseInt(currentObjectKeys[currentObjectKeys.length - 1]) + 1
myObject = Object.assign({
[nextObjectKey]: {
"category": "myCategory3",
"title": "myTitle3"
}
}, myObject)
delete myObject['1'];
//right here sort myObject by key then print myObject but say if the keys are 0 & 2
//I want to change the keys to 0 & 1 and this should work with any length of nested objects
console.log(myObject)
If you want a data structure whose integer keys start at 0 and do not have holes, you should use an array, not an object - then all you need to do is splice the value out of the array, and the rest will be re-arranged appropriately. Pushing a value becomes much easier too.
const categories = [{
"category": "myCategory1",
"title": "myTitle1"
}, {
"category": "myCategory2",
"title": "myTitle2"
}];
// instead of nextObjectKey and Object.assign, just do:
categories.push({
"category": "myCategory3",
"title": "myTitle3"
});
// instead of delete and resort, do:
categories.splice(1, 1);
// (index to remove, number of values to remove)
console.log(categories);
I would agree, not sure an object is the right tool here, not knowing the background. But to answer the question as posted:
const originalObject = {
0: {
"category": "myCategory1",
"title": "myTitle1"
},
3: {
"category": "myCategory3",
"title": "myTitle3"
},
7: {
"category": "myCategory7",
"title": "myTitle7"
}
};
const newObject = Object.fromEntries(
Object.entries(originalObject)
.sort(([k,v]) => k)
.map(([k,v], i) => [i,v])
);
console.log(newObject);
What's up, I've got a general js question that I've been struggling with. Not quite sure how to do this, but I have an array of objects like this
{
"postHeight": "5",
"type": "CL",
"barb": "None"
(Actual object will have around 20-30 properties to search through)
}
I also have a search function that allows you to search through these objects to find the right one. There will be hundreds of these objects in the array, so if I want to find one specific object with a postHeight of 6, a type of CL, and a barb of straight, it will find the object where all of those match up, and then push that entire object that matched, to a different array. Easy enough. My main problem is that I want to be able to tell the user, if only objects' postHeight and type match their inputs without barb matching their inputs, that No exact matches found, but there is a match that has the correct postHeight and type, just with an incorrect barb.
I'm not quite sure how I should be going about doing that, though. There could be 15 objects with matching postHeights and 20 with matching types, but I want to find the object with the most amount of matches, and then tell the users exactly which properties match, and which ones don't match their search query.
The object used in this question only has 3 properties but we'll most likely have more around the 20-30 range for each object
You could sort the array of objects by how many fields match:
const data = [
{
"postHeight": "6",
"type": "CL",
"barb": "straight"
},
{
"postHeight": "5",
"type": "CL",
"barb": "straight"
},
{
"postHeight": "6",
"type": "SA",
"barb": "straight"
},
{
"postHeight": "6",
"type": "CL",
"barb": "None"
}
]
const search = (obj) => {
const entries = Object.entries(obj);
const searchKeys = Object.keys(obj);
return data.map((el) => {
const matched = entries.filter(([k, v]) => el[k] === v);
return { count: matched.length, ...el, unmatched: searchKeys.filter(k => el[k] !== obj[k]) }
}).sort((a, b) => b.count - a.count);
}
console.log(search({postHeight: "6", type: "CL", barb: "straight"}));
I want to fetch all the names and label from JSON without loop. Is there a way to fetch with any filter method?
"sections": [
{
"id": "62ee1779",
"name": "Drinks",
"items": [
{
"id": "1902b625",
"name": "Cold Brew",
"optionSets": [
{
"id": "45f2a845-c83b-49c2-90ae-a227dfb7c513",
"label": "Choose a size",
},
{
"id": "af171c34-4ca8-4374-82bf-a418396e375c",
"label": "Additional Toppings",
},
],
},
]
}
When you say "without loops" I take it as without For Loops. because any kind of traversal of arrays, let alone nested traversal, involve iterating.
You can use the reduce method to have it done for you internally and give you the format you need.
Try this :
const data = {
sections: [
{
id: "62ee1779",
name: "Drinks",
items: [
{
id: "1902b625",
name: "Cold Brew",
optionSets: [
{
id: "45f2a845-c83b-49c2-90ae-a227dfb7c513",
label: "Choose a size"
},
{
id: "af171c34-4ca8-4374-82bf-a418396e375c",
label: "Additional Toppings"
}
]
}
]
}
]
};
x = data.sections.reduce((acc, ele) => {
acc.push(ele.name);
otherName = ele.items.reduce((acc2, elem2) => {
acc2.push(elem2.name);
label = elem2.optionSets.reduce((acc3, elem3) => {
acc3.push(elem3.label);
return acc3;
}, []);
return acc2.concat(label);
}, []);
return acc.concat(otherName);
}, []);
console.log(x);
Go ahead and press run snippet to see if this matches your desired output.
For More on info reduce method
In the context of cJSON
yes, we can fetch the key value for any of the object.
1 - each key value is pointed by one of the objects. will simply fetch that object and from there will get the key value.
In the above case for
pre-requisition: root must contain the json format and root must be the cJSON pointer. if not we can define it and use cJSON_Parse() to parse the json.
1st name object is "sections" will use
cJSON *test = cJSON_GetObjectItem(root, "sections");
char *name1 = cJSON_GetObjectItem(test, "name" )->valuestring;
2nd name key value
cJSON *test2 = cJSON_GetObjectItem(test, "items");
char *name2 = cJSON_GetObjectItem(tes2, "name")->valuestring;
likewise, we can do for others as well to fetch the key value.
I have a JSON array like this:
[
{
"id": "1",
"name": "A"
},
{
"id": "2",
"name": "B"
},
{
"id": "3",
"name": "C"
},
{
"id": "4",
"name": "D"
},
....
....
{
"id": "n",
"name": "X"
}
]
I'm looking for a slice() based function that gives the last 20 item of this JSON array
function getLast(array,x){return array.slice(array.length-x)}
Just use the slice function starting with the array length minus the number of elements you want to extract.
A simple way with filters:
filteredList = list.filter((_, index) => {
return index >= list.length - 20
})
If you just need the last X items in an array I'm not sure you need filter, you can use .slice eg [1,2,3,4,5,6,7,8,9,10].slice(-5) will return [6,7,8,9,10]
One option is to use splice or slice:
// Initialize array
let arr = new Array(50).fill().map((v,i)=>i)
// Pick off last 20 elements
console.log('Last:' + arr.slice(-20))
Note: splice modifies the existing array; if you don't want to modify the existing array use slice
Another example
let arr = new Array(50).fill().map((v,i)=>i+1) // [1,2,..50]
Array.prototype.last = function(n){
return this.slice(-n)
};
console.log( arr.last(20) )
This is the sample json:
{
"search": {
"facets": {
"author": [
],
"language": [
{
"value": "nep",
"count": 3
},
{
"value": "urd",
"count": 1
}
],
"source": [
{
"value": "West Bengal State Council of Vocational Education & Training",
"count": 175
}
],
"type": [
{
"value": "text",
"count": 175
}
],
}
}
There are several ways to delete key search.facets.source:
delete search.facets.source
delete jsobObj['search']['facets']['source']
var jsonKey = 'source';
JSON.parse(angular.toJson(jsonObj), function (key, value) {
if (key != jsonKey)
return value;
});
Above 1 & 2 are not dynamic, and 3 is one of the way but not a proper way. Because if source is present in another node then it will not work. Please anybody can tell me how to delete it dynamically in any kind of nested key. Because we can not generate sequence of array dynamically in above 2.
Assuming you're starting from this:
let path = 'search.facets.source';
Then the logic is simple: find the search.facets object, then delete obj['source'] on it.
Step one, divide the path into the initial path and trailing property name:
let keys = path.split('.');
let prop = keys.pop();
Find the facets object in your object:
let parent = keys.reduce((obj, key) => obj[key], jsonObj);
Delete the property:
delete parent[prop];
I have found out another solution, it is very easy.
var jsonKey = 'search.facets.source';
eval('delete jsonObj.' + jsonKey + ';');