nika
nika

Reputation: 135

How to sort array of objects in Javascript by their keys in order?

I have an array of objects like this:

 const jData = [
    {
      price: "500",
      count: "10",
      left: "150"
    },
    {
      left: "75",
      price: "350",
      count: "40"
    },
    {
      count: "200",
      left: "50",
      price: "7500"
    }
  ];

and array of orderedData like this :

orderedData = ["price", "count", "left"]

I'm trying to sort my array of objects (jData) by keys so that keys will be in the same order as orderedData.

So far my code looks like this:

import "./styles.css";

export default function App() {
  const jData = [
    {
      price: "500",
      count: "10",
      left: "150"
    },
    {
      left: "75",
      price: "350",
      count: "40"
    },
    {
      count: "200",
      left: "50",
      price: "7500"
    }
  ];

  const orderedData = ["price", "count", "left"];

  let res = jData?.flatMap((x) => Object.keys(x));

  var unique = res.filter(function (elem, index, self) {
    return index === self.indexOf(elem);
  });

  const keys = unique?.filter((key) => orderedData.includes(key));
  
  console.log(keys)

  let newData = jData.sort(
    (a, b) =>
      orderedData.indexOf(a) - orderedData.indexOf(b)
  )
  
  console.log(newData)
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

codesandbox

I was able to get keys from my array of objects and sort them in the correct order, when I'm console logging keys

  console.log(keys); => output is 

["price"
1: "count"
2: "left"] // same order as in orderedData = ["price", "count", "left"]

but when I'm trying to sort my jData array so that keys in objects will be positioned in the same order, nothing changes.

let newData = jData.sort(
    (a, b) =>
      orderedData.indexOf(a) - orderedData.indexOf(b)
  )
 console.log(newData) => outputs objects with the same positioned keys.

What I would love to achieve is to display objects like this:

const jData = [
    {
      price: "500",
      count: "10",
      left: "150"
    },
    {
      price: "350",
       count: "40",
      left: "75",
     
    },
    {
      price: "7500"
      count: "200",
      left: "50",
     
    }
  ];

I don't know if it's possible to do in Javascript? Thank you for any tip/suggestion.

Upvotes: 1

Views: 1274

Answers (4)

roperzh
roperzh

Reputation: 960

As @ggorlen suggested, the order of keys in an object is not guaranteed. You could iterate over orderedData to access the keys from each object in the order you need:

jData.map((item) => (
  <ul>
    orderedData.map((key) => <li>{key}: {item[key]}</li>)
  </ul>
));

Upvotes: 1

ggorlen
ggorlen

Reputation: 56865

Relying on object key order is an antipattern, even when an implementation guarantees insertion order. Objects are traditionally collections of key-value pairs, suitable for lookup by key but not as friendly to iteration or sorting as arrays.

Instead of ordering the object, the general approach to tabular data like this is to provide the array orderedData alongside the object and, when you need the object's keys in a particular order, map along orderedData and key into each column of the row. You can think of orderedData as headers or column names of the table and jData as row data.

Here's a proof of concept you can extrapolate to your use case (I realize you're probably not creating the exact component Table, but it illustrates my point):

const Table = ({rows, columns}) =>
  <table>
    <tbody>
      {columns.map((name, i) => 
        <th key={name + i}>{name}</th>
      )}
      {rows.map((row, i) =>
        <tr key={i}>
          {columns.map((name, i) => 
            <td key={name + i}>{row[name]}</td>
          )}
        </tr>
      )}
    </tbody>
  </table>
;

const jData = [
  {
    price: "500",
    count: "10",
    left: "150"
  },
  {
    left: "75",
    price: "350",
    count: "40"
  },
  {
    count: "200",
    left: "50",
    price: "7500"
  }
];
const orderedData = ["price", "count", "left"];
ReactDOM.render(
  <Table rows={jData} columns={orderedData} />,
  document.body
);
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>

Upvotes: 0

Vinibr
Vinibr

Reputation: 834

I guess this is the best solution for you. Check this out.

const jData = [
    {
      price: "500",
      count: "10",
      left: "150"
    },
    {
      left: "75",
      price: "350",
      count: "40"
    },
    {
      count: "200",
      left: "50",
      price: "7500"
    }
  ];


const orderedData = jData.map(x=> {
    return Object.entries(x)
    .sort(([,a],[,b]) => b-a)
    .reduce((r, [k, v]) => ({ ...r, [k]: v }), {});
})

console.log(orderedData)

Upvotes: 1

Mocuishle
Mocuishle

Reputation: 174

You can try this:

 function orderedDataByCustomizedKey(jData,orderedData) {
        const newjData = [];
        jData.forEach((item, i) => {
            const temObj = {};
            orderedData.forEach((orderedDataItem, index) => {
                temObj[orderedDataItem] = item[orderedDataItem];
            })
            newjData.push(temObj);
        })
        return newjData;
    }

Upvotes: 1

Related Questions