Reputation: 479
I have this object I want to sort and filter by retaining only the 2 highest values by object.
obj={ A :[{
asset: 9,
biodiversity: 4,
infrastructure: 15,
deflation: 11,
energy: 9
}],
B:[{
asset: 12,
biodiversity: 10,
infrastructure: 9,
deflation: 7,
energy: 15
}],
C:[{
asset: 2,
biodiversity: 12,
infrastructure: 6,
deflation: 6,
energy: 8
}]}
I would like to sort the objects by their values and filter out the 2 highest e.g:
{A :[{
infrastructure: 15,
deflation: 11
}],
B:[{
energy: 15,
asset: 12
}],
C:[{
biodiversity: 12,
energy: 8
}]}
I have tried this for sorting:
Object.keys(obj).forEach((a) => _.sortBy(obj[a][0])))
But that is wrong obviously. I am using lodash but will accept vanilla javascript solution as well.
Upvotes: 2
Views: 497
Reputation: 4236
(Edit/Note: This is based on the code you originally posted. I'm glad to see you updated your question and got rid of the wrapping array.)
Here's a relatively functional approach.
The secondLargestValue
function finds the threshold value within each object.
The copyWithoutValsBelowThreshold
function gives us a modified copy of the object.
We loop though the entries in the top-level object and apply these two functions. See comments in the code for further clarification.
let json = getArray(); // Identifies the original array
// Defines `secondLargestVal` function
const secondLargestVal = someObj =>
// Gets second item of array after sorting numerically in descending order
Object.values(someObj).sort((a, b) => b - a)[1];
// Defines `copyWithoutValsBelowThreshold` function
const copyWithoutValsBelowThreshold = ( (someObj, threshold) => {
// This function doesn't mutate the original value
clone = Object.assign({}, someObj); // Copies obj
for(let prop in clone){
// Loops through properties, deleting non-qualifying ones
if(clone[prop] < threshold){
delete clone[prop];
}
}
return clone;
});
// Defines our main function
const mutateJson = () => {
let entries = Object.entries(json[0]);
entries = entries.map(entry => {
// `entry[0]` is the property name (eg: 'A') -- we don't actually use this
// `entry[1]` is the value (in this case, an array containing a single object)
let obj = entry[1][0]; // Identifies the actual object
const threshold = secondLargestVal(obj); // Identifies minimum value
// Updates the entry, deleting properties whose values are too low
entry[1][0] = copyWithoutValsBelowThreshold(obj, threshold);
return entry;
});
json[0] = Object.fromEntries(entries); // Replaces the top-level object
}
// Calls the main function
mutateJson();
console.log(json);
// Provides the original array
function getArray(){
return [{ A :[{
asset: 9,
biodiversity: 4,
infrastructure: 15,
deflation: 11,
energy: 9
}],
B:[{
asset: 12,
biodiversity: 10,
infrastructure: 9,
deflation: 7,
energy: 15
}],
C:[{
asset: 2,
biodiversity: 12,
infrastructure: 6,
deflation: 6,
energy: 8
}]}]
}
Upvotes: 0
Reputation: 386654
You could get the entries of the inner objects and sort by value descending, get the top two key/value pairs and build a new object from it.
const
data = { A: [{ asset: 9, biodiversity: 4, infrastructure: 15, deflation: 11, energy: 9 }], B: [{ asset: 12, biodiversity: 10, infrastructure: 9, deflation: 7, nergy: 15 }], C: [{ asset: 2, biodiversity: 12, infrastructure: 6, deflation: 6, energy: 8 }]},
result = Object.fromEntries(Object
.entries(data)
.map(([k, a]) => [k, a.map(o => Object.fromEntries(Object
.entries(o)
.sort((a, b) => b[1] - a[1])
.slice(0, 2)
))])
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 2