sort nested object to be consecutive and ascending - javascript

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);

Related

Destructuring object with inner array without all keys

I have an object like this:
const objBefore:
{
"id": "3pa99f64-5717-4562-b3fc-2c963f66afa1",
"number": "5000",
"enabled": true,
"classes": [
{
"id": "2fc87f64-5417-4562-b3fc-2c963f66afa4",
"name": "General"
},
{
"id": "7ffcada8-0215-4fb0-bea9-2266836d3b18",
"name": "Special"
},
{
"id": "6ee973f7-c77b-4738-b275-9a7299b9b82b",
"name": "Limited"
}
]
}
Using es6, I want to grab everything in the object except the name key of the inner classes array to pass it to an api.
So:
{
"id": "3pa99f64-5717-4562-b3fc-2c963f66afa1",
"number": "5000",
"enabled": true,
"classes": [
{"id": "2fc87f64-5417-4562-b3fc-2c963f66afa4"},
{"id": "7ffcada8-0215-4fb0-bea9-2266836d3b18"},
{"id": "6ee973f7-c77b-4738-b275-9a7299b9b82b"}
]
}
The closest I got was: let {id, number, enabled, classes: [{id}]} = objBefore;
But it only gets me one id in classes. I've tried spreading above using [...{id}] or [{...id}]. Same thing.
I find it challenging to get the right mental model for how to think about this when it's on multiple levels. In my mind, when I say [...{id}] I'm thinking, "I want the id property as an object in the outer classes array, but give me every id in the array!"
Clearly I'm not thinking about this correctly.
I've tried it using map to get that part but I'm still having trouble combining it back to the original to produce the desired result. for example:
let classIds = objBefore.classes.map(({id}) => {
return {
id
}
})
(Using the map syntax, how can I destructure in the function the other keys that are one level higher?)
To combine them I started trying anything and everything, :
let {id, number, enabled, classIds} = {objBefore, [...classIds]} // returns undefined for all
I'd prefer to do it in one statement. But if that's not possible, then what's a clean way to do it using map?.
You can't destructure and map at the same time in the way you're looking to do it. The main purpose of destructuring assignment is to extract data from an array/object and not for manipulating data. In your case, as you're after an object with the same keys/value as your original object, just with a different classes array, I would instead suggest creating a new object and spreading ... the original object into that. Then you can overwrite the classes array with a mapped version of that array:
const objBefore = { "id": "3pa99f64-5717-4562-b3fc-2c963f66afa1", "number": "5000", "enabled": true, "classes": [ { "id": "2fc87f64-5417-4562-b3fc-2c963f66afa4", "name": "General" }, { "id": "7ffcada8-0215-4fb0-bea9-2266836d3b18", "name": "Special" }, { "id": "6ee973f7-c77b-4738-b275-9a7299b9b82b", "name": "Limited" } ] };
const newObj = {
...objBefore,
classes: objBefore.classes.map(({id}) => ({id}))
};
console.log(newObj);
How about using simple util method with object destructuring, spread operator and map
const objBefore = {
id: "3pa99f64-5717-4562-b3fc-2c963f66afa1",
number: "5000",
enabled: true,
classes: [
{
id: "2fc87f64-5417-4562-b3fc-2c963f66afa4",
name: "General",
},
{
id: "7ffcada8-0215-4fb0-bea9-2266836d3b18",
name: "Special",
},
{
id: "6ee973f7-c77b-4738-b275-9a7299b9b82b",
name: "Limited",
},
],
};
const process = ({ classes, ...rest }) => ({
...rest,
classes: classes.map(({ id }) => ({ id })),
});
console.log(process(objBefore))
In one line, you could do this:
const objAfter = { ...objBefore, classes: objBefore.classes.map(item => ({ id: item.id })) };
Or, if you prefer:
const objAfter = {...objBefore, classes: objBefore.classes.map(({id}) => ({id}))};
There isn't any way in object destructing to copy an entire array of objects into a different array of objects by removing properties so you use .map() for that.

Get the last elements in a JSON array in Javascript

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) )

JS dynamically access & delete nested object property [duplicate]

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 + ';');

Combining values in nested objects with common properties

# 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);

Keep the sort order

I have a object with that values :
category_list = {
"1000":{
"name":"Cars",
"order":"1",
"level": "2"
},
"2010":{
"name":"Houses",
"order":"2",
"level": "2"
},
"1030":{
"name":"Cars",
"order":"3",
"level": "2"
}
}
And when I would like to show it Chrome reorders it based on the Index :
It becomes :
category_list = {
"1000":{
"name":"Cars",
"order":"1",
"level": "2"
},
"1030":{
"name":"Cars",
"order":"3",
"level": "2"
},
"2010":{
"name":"Houses",
"order":"2",
"level": "2"
}
}
I wish to keep the order as it was when pushing! or reorder based on field "order"
Can someone please help with that?
JavaScript objects are by definition unordered.
If you need an ordered list, you should use an array (of objects) instead, e.g.:
var objs = [
{
"key": 1000,
"name":"Cars",
"order": 1,
"level": 2
}, ...
];
objs.sort(function(a, b) {
return a.order - b.order;
});
NB: for numeric properties use numeric types.
JavaScript objects do not guarantee a specific order for their attributes. So the structure you'd like to have simply doesn't exist in JavaScript.
So with the native structures you can get either:
Array: Guaranteed order, but only accessing elements sequentially or by a numeric (0..n-1) index
Object: Arbitrary order, but you can access elements sequentially (again, arbitrary order) or using its key (which can be any string)
If you need both you either need to add an array that maps the order to the object keys, e.g. [1000, 2010, 1030] or store the data in an array and create a mapping like this: {1000: 0, 2010: 1, 1030: 2}.

Categories

Resources