road
road

Reputation: 479

sort and filter an object array with lodash (or vanilla javascript)

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

Answers (2)

Cat
Cat

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

Nina Scholz
Nina Scholz

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

Related Questions