Get the last elements in a JSON array in Javascript - 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) )

Related

sort nested object to be consecutive and ascending

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

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

replace multiple values in json/jsObject/string

I have a response from a web service and want to replace some values in the response with my custom values.
One way is to write a tree traverser and then check for the value and replace with my custom value
so the response is some what like this:
[
{
"name": "n1",
"value": "v1",
"children": [
{
"name": "n2",
"value": "v2"
}
]
},
{
"name": "n3",
"value": "v3"
}
]
now my custom map is like this
const map = {
"v1": "v11",
"v2": "v22",
"v3": "v33"
};
All I want is
[
{
"name": "n1",
"value": "v11",
"children": [
{
"name": "n2",
"value": "v22"
}
]
},
{
"name": "n3",
"value": "v33"
}
]
I was thinking if I could stringify my response and then replace values using a custom build regex from my map of values.
Will it be faster as compared to tree traverser?
If yes, how should I do that?
somewhat like this
originalString.replace(regexp, function (replacement))
The tree traversal is faster
Note that some things could be done more efficiently in the regex implementation but I still think there are some more bottlenecks to explain.
Why the regex is slow:
There are probably many more reasons why the regex is slower but I'll explain at least one significant reason:
When you're using regex to find and replace, you're using creating new strings every time and performing your matches every time. Regex expressions can be very expensive and my implementation isn't particularly cheap.
Why is the tree traversal faster:
In the tree traversal, I'm mutating the object directly. This doesn't require creating new string objects or any new objects at all. We're also not performing a full search on the whole string every time as well.
RESULTS
run the performance test below. The test using console.time to record how long it takes. See the the tree traversal is much faster.
function usingRegex(obj, map) {
return JSON.parse(Object.keys(map).map(oldValue => ({
oldValue,
newValue: map[oldValue]
})).reduce((json, {
oldValue,
newValue
}) => {
return json.replace(
new RegExp(`"value":"(${oldValue})"`),
() => `"value":"${newValue}"`
);
}, JSON.stringify(obj)));
}
function usingTree(obj, map) {
function traverse(children) {
for (let item of children) {
if (item && item.value) {
// get a value from a JS object is O(1)!
item.value = map[item.value];
}
if (item && item.children) {
traverse(item.children)
}
}
}
traverse(obj);
return obj; // mutates
}
const obj = JSON.parse(`[
{
"name": "n1",
"value": "v1",
"children": [
{
"name": "n2",
"value": "v2"
}
]
},
{
"name": "n3",
"value": "v3"
}
]`);
const map = {
"v1": "v11",
"v2": "v22",
"v3": "v33"
};
// show that each function is working first
console.log('== TEST THE FUNCTIONS ==');
console.log('usingRegex', usingRegex(obj, map));
console.log('usingTree', usingTree(obj, map));
const iterations = 10000; // ten thousand
console.log('== DO 10000 ITERATIONS ==');
console.time('regex implementation');
for (let i = 0; i < iterations; i += 1) {
usingRegex(obj, map);
}
console.timeEnd('regex implementation');
console.time('tree implementation');
for (let i = 0; i < iterations; i += 1) {
usingTree(obj, map);
}
console.timeEnd('tree implementation');
Will it be faster as compared to tree traverser?
I don't know. I think it would depend on the size of the input, and the size of the replacement map. You could run some tests at JSPerf.com.
If yes, how should I do that?
It's fairly easy to do with a regex-based string replacement if the values you are replacing don't need any special escaping or whatever. Something like this:
const input = [
{
"name": "n1",
"value": "v1",
"children": [
{
"name": "n2",
"value": "v2"
}
]
},
{
"name": "n3",
"value": "v3"
}
];
const map = {
"v1": "v11",
"v2": "v22",
"v3": "v33"
};
// create a regex that matches any of the map keys, adding ':' and quotes
// to be sure to match whole property values and not property names
const regex = new RegExp(':\\s*"(' + Object.keys(map).join('|') + ')"', 'g');
// NOTE: if you've received this data as JSON then do the replacement
// *before* parsing it, don't parse it then restringify it then reparse it.
const json = JSON.stringify(input);
const result = JSON.parse(
json.replace(regex, function(m, key) { return ': "' + map[key] + '"'; })
);
console.log(result);
definitely traverser go faster as string replace means travels against each characters in the final string as opposed to iterator that can skips no necessarily item.

How to sort an array of objects in ascending order of number?

I have an array of objects like the following :
var array = {
"112" : {
"id": "3",
"name": "raj"
},
"334" : {
"id": "2",
"name": "john"
},
"222" : {
"id": "5",
"name": "kelvin"
}
}
Now i want to sort the array in ascending order of id and then restore it in array. I tried using sort() but could not do it. Please help how to do so that when i display the data from the array it comes sorted.
Assuming you meant your code to be an array of objects, ie:
var unsortedArray = [
{ id: 3, name: "raj" },
{ id: 2, name: "john" },
{ id: 5, name: "kelvin" }
];
Then you would be able to sort by id by passing a function to Array.sort() that compares id's:
var sortedArray = unsortedArray.sort(function(a, b) {
return a.id - b.id
});
As others have pointed out, what you have is an object containing objects, not an array.
var array = {
"112" : {
"id": "3",
"name": "raj"
},
"334" : {
"id": "2",
"name": "john"
},
"222" : {
"id": "5",
"name": "kelvin"
}
}
var sortedObject = Array.prototype.sort.apply(array);
result:
{
"112": {
"id": "3",
"name": "raj"
},
"222": {
"id": "5",
"name": "kelvin"
},
"334": {
"id": "2",
"name": "john"
}
}
That isn't an array, it is an object (or would it if it wasn't for the syntax errors (= should be :)). It doesn't have an order.
You could use an array instead (making the current property names a value of a key on the subobjects).
Alternatively, you could use a for loop to build an array of the key names, then sort that and use it as a basis for accessing the object in order.
JavaScript objects are unordered by definition. The language specification doesn't even guarantee that, if you iterate over the properties of an object twice in succession, they'll come out in the same order the second time.
If you need things to be ordered, use an array and the Array.prototype.sort method.
That is an object but you can sort an array ilke this:
Working Demo: http://jsfiddle.net/BF8LV/2/
Hope this help,
code
function sortAscending(data_A, data_B)
{
return (data_A - data_B);
}
var array =[ 9, 10, 21, 46, 19, 11]
array.sort(sortAscending)
alert(array);​
Not many people knows that Array.sort can be used on other kinds of objects, but they must have a length property:
array.length = 334;
Array.prototype.sort.call(array, function(a, b) {return a.id - b.id;});
Unfortunately, this doesn't work well if your "array" is full of "holes" like yours.

Categories

Resources