James Mulholland
James Mulholland

Reputation: 1872

Sorting multiple rows of data in JavaScript with lodash

I have some data that I wish to sort into a table. The data consists of an array of rows, which are in turn arrays of cells, which are objects with the properties column_name, column_value, column_type and column_id (as shown below).

I would like to sort the data by multiple columns, e.g. I would like to sort by city ascending and then by age descending. I believe that lodash's _.orberBy() can achieve this, but I can't work out how to implement it on nested data like this.

Although I suspect lodash will get the job done, I suspect there might be another way that is better.

How would I go about sorting the following, nested data by different columns?

import _ from 'lodash';

const sampleData = [
  [
    {
      column_name: 'id',
      column_value: '12345',
      column_type: 'string',
      column_id: 'item_attributes#id',
    },
    {
      column_name: 'age',
      column_value: '32',
      column_type: 'number',
      column_id: 'item_attributes#age',
    },
    {
      column_name: 'city',
      column_value: 'London',
      column_type: 'string',
      column_id: 'item_attributes#city',
    },
  ],
  [
    {
      column_name: 'id',
      column_value: 'abcde',
      column_type: 'string',
      column_id: 'item_attributes#id',
    },
    {
      column_name: 'age',
      column_value: '52',
      column_type: 'number',
      column_id: 'item_attributes#age',
    },
    {
      column_name: 'city',
      column_value: 'Bristol',
      column_type: 'string',
      column_id: 'item_attributes#city',
    },
  ],
  [
    {
      column_name: 'id',
      column_value: 'a1b2d',
      column_type: 'string',
      column_id: 'item_attributes#id',
    },
    {
      column_name: 'age',
      column_value: '21',
      column_type: 'number',
      column_id: 'item_attributes#age',
    },
    {
      column_name: 'city',
      column_value: 'London',
      column_type: 'string',
      column_id: 'item_attributes#city',
    },
  ],
];

const orderedData = _.orderBy(
  sampleData,
  // ?? what goes here?
  // ?? what goes here?
);

// desired output
// [
//   [
//     {
//       column_name: 'id',
//       column_value: 'abcde',
//       column_type: 'string',
//       column_id: 'item_attributes#id',
//     },
//     {
//       column_name: 'age',
//       column_value: '52',
//       column_type: 'number',
//       column_id: 'item_attributes#age',
//     },
//     {
//       column_name: 'city',
//       column_value: 'Bristol',
//       column_type: 'string',
//       column_id: 'item_attributes#city',
//     },
//   ],
//   [
//     {
//       column_name: 'id',
//       column_value: '12345',
//       column_type: 'string',
//       column_id: 'item_attributes#id',
//     },
//     {
//       column_name: 'age',
//       column_value: '32',
//       column_type: 'number',
//       column_id: 'item_attributes#age',
//     },
//     {
//       column_name: 'city',
//       column_value: 'London',
//       column_type: 'string',
//       column_id: 'item_attributes#city',
//     },
//   ],
//   [
//     {
//       column_name: 'id',
//       column_value: 'a1b2d',
//       column_type: 'string',
//       column_id: 'item_attributes#id',
//     },
//     {
//       column_name: 'age',
//       column_value: '21',
//       column_type: 'number',
//       column_id: 'item_attributes#age',
//     },
//     {
//       column_name: 'city',
//       column_value: 'London',
//       column_type: 'string',
//       column_id: 'item_attributes#city',
//     },
//   ],
// ]

Upvotes: 3

Views: 495

Answers (2)

OliverRadini
OliverRadini

Reputation: 6466

I think this should do what you need. It's easiest to convert each row to a more pleasant object before attempting to use lodash to sort, but we can do that as part of lodash's orderBy function:

const sampleData = [
  [
    {
      column_name: 'id',
      column_value: '12345',
      column_type: 'string',
      column_id: 'item_attributes#id',
    },
    {
      column_name: 'age',
      column_value: '32',
      column_type: 'number',
      column_id: 'item_attributes#age',
    },
    {
      column_name: 'city',
      column_value: 'London',
      column_type: 'string',
      column_id: 'item_attributes#city',
    },
  ],
  [
    {
      column_name: 'id',
      column_value: 'abcde',
      column_type: 'string',
      column_id: 'item_attributes#id',
    },
    {
      column_name: 'age',
      column_value: '52',
      column_type: 'number',
      column_id: 'item_attributes#age',
    },
    {
      column_name: 'city',
      column_value: 'Bristol',
      column_type: 'string',
      column_id: 'item_attributes#city',
    },
  ],
  [
    {
      column_name: 'id',
      column_value: 'a1b2d',
      column_type: 'string',
      column_id: 'item_attributes#id',
    },
    {
      column_name: 'age',
      column_value: '21',
      column_type: 'number',
      column_id: 'item_attributes#age',
    },
    {
      column_name: 'city',
      column_value: 'London',
      column_type: 'string',
      column_id: 'item_attributes#city',
    },
  ],
];

const rowToNiceObject = row => row.reduce((prev, curr) => {
  const thisKey = curr['column_name'];
  const thisValue = curr['column_value'];
  
  const newObject = {};
  newObject[thisKey] = thisValue;
  
  return Object.assign(prev, newObject);
}, {});

const result = _.orderBy(sampleData, [
  row => rowToNiceObject(row).city,
  row => rowToNiceObject(row).age
], [
  'asc',
  'desc'
]);

console.dir(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>

Upvotes: 1

Lars Holdaas
Lars Holdaas

Reputation: 750

Personally I think you should map your data structure a bit to better suit javascript operations, such as { id: '12345', age: 32, city: 'London'}. The values can hold the type.

However, with your current data structure you could do sorting like this:

const orderedData = _.orderBy(
  sampleData,
  entry => entry.find(valueObj => valueObj.column_name==='age').column_value
);

This example uses your 'age' property. It works by finding the entry in your object collection array, getting the name that describes the age, then comparing by that column_value.

Replacing it with 'city' or 'id' works fine.

Upvotes: 2

Related Questions