0

I want to convert an Array of Arrays to one object with the same sort in javascript. My problem is the keys are in numbers and i just sorted the values as i want but when i convert the array to object by useing reduce it sort it again by keys.

const names = {
1: 'ياسر',
3: 'احمد'
}

const sorting = Object.entries(names)
               .sort((a, b) => a[1].localeCompare(b[1], "ar", { ignorePunctuation: true }))
               .reduce((acc, x) {
                 acc[x[0]] = x[1];
                 return acc;
                              }, {});
  • For English speaking it's hard to understand from your example what is the result and what actually desired. Also, your example is not valid javascript. Please use snippet button to post working example. – vanowm Mar 27 '22 at 16:59
  • 1
    integer properties in Objects always come first and are sorted ascending. see: [Does JavaScript guarantee object property order?](https://stackoverflow.com/questions/5525795/does-javascript-guarantee-object-property-order#:~:text=YES%20(but%20not%20always%20insertion%20order).&text=Symbol%20names%2C%20in%20insertion%20order,this%20and%20all%20browsers%20comply). If you want a sorted collection with integer keys use an Array. – pilchard Mar 27 '22 at 17:17

1 Answers1

1

Objects don't preserve order of insertion when keys parsed as integers. So you can only append a string to the keys or use Map instead.

const names = {
3: 'D',
7: 'A',
5: 'B',
8: 'C'
}
names["4"] = "E";
console.log("names", names);

const object = Object.entries(names)
               .sort((a, b) => a[1].localeCompare(b[1], "ar", { ignorePunctuation: true }))
               .reduce((acc, x) => {
                acc[x[0]] = x[1];
                 return acc;
               }, {});
console.log("as object", object);
const objectString = Object.entries(names)
               .sort((a, b) => a[1].localeCompare(b[1], "ar", { ignorePunctuation: true }))
               .reduce((acc, x) => {
                acc["_" + x[0]] = x[1];
                 return acc;
               }, {});

console.log("as object w/string", objectString);
const map = Object.entries(names)
               .sort((a, b) => a[1].localeCompare(b[1], "ar", { ignorePunctuation: true }))
               .reduce((acc, x) => {
                acc.set(x[0], x[1]);
                 return acc;
               }, new Map());
console.log("as map", map, "look in devtools");

[EDIT] There is a way utilize proxy, which will allow "hide" prefix of the keys:

const names = {
3: 'D',
7: 'A',
5: 'B',
8: 'C'
}
names["4"] = "E";
console.log("names", names);

function sortObject(obj)
{
  const prefix = "___";
  return new Proxy(
    /* proxy target */
    Object.entries(obj)
     .sort((a, b) => a[1].localeCompare(b[1], "ar", { ignorePunctuation: true }))
     .reduce((acc, x) => (acc[prefix + x[0]] = x[1], acc), {})
    ,
    { /* proxy handler */
      get(target, key)
      {
        return typeof key == "string" ? target[prefix + key] : target[key];
      },
      set(target, key, value)
      {
        target[prefix + key] = value;
      },
      ownKeys: target => Object.keys(target).map(e => e.substring(prefix.length)), //remove prefix
      getOwnPropertyDescriptor: (target, key) => ({ enumerable: true, configurable: true })
    });
}
let proxy = sortObject(names);

proxy[0] = "AA"; //new properties added to the end

console.log("proxy added new entry to the end", proxy);

proxy = sortObject(proxy); //we can sort it again

console.log("proxy sorted with new entry", proxy);

console.log("it's enumerable too!");
for(i in proxy)
  console.log("key:", i, "value:", proxy[i]);
vanowm
  • 9,466
  • 2
  • 21
  • 37
  • Thanks, Vanowm my case needs the keys to be integers as it's used as a lookup column in material-table so would it work if after converting the keys with a string to move the string and keep the sort. –  Mar 27 '22 at 18:46
  • Wait, if it's a look up table, why do you need an order than? Can you give an example how you are planning on using it? – vanowm Mar 27 '22 at 19:50
  • I have a material table and am using react so i have a column in a table of many relations and i just get the relation id and am using a lookup whom just taking an object so to be able to retrieve names in this column as a Combobox menu with all names and i just need to sort these names Asc to be able to search easy when inserting new raw –  Mar 27 '22 at 20:11
  • perhaps proxy method would work for you (added example) – vanowm Mar 27 '22 at 21:29
  • Hi vanowm i tried another method to achieve what i want i just set the Autocomplete from material and it's working great for what i wanted but the problem with me is when i add SetState in the onChange it's get out from edit mode ``` editComponent: (props) => ( a.name.localeCompare(b.name, "ar", { ignorePunctuation: true }) ) .map((el) => el.name)} onChange={(e, value) =>setCurrentOwnerId(value) /> ``` –  Mar 29 '22 at 07:03
  • Sorry, I don't know what "get out from edit mode" means...it's sounds outside of current topic scope ;) – vanowm Mar 29 '22 at 11:26