javascriptarrayssortingobjectobject-comparison

Sorting array containing objects using complex/dynamic criteria (ascending and descending)?


We have an array with data and I want to sort it by users key:

Example:

DESCENDING: Zorya, Gorya, Dorya, Borya, Aorya, 4, 2, 0

 ASCENDING: Aorya, Borya, Dorya, Gorya, Zorya, 2, 4, 0

This is what I've done so far:

const array = [{
  name: "qw",
  users: [
    { name: "Borya" },
  ],
}, {
  name: "qw",
  users: [
    { name: "Gorya" },
  ],
}, {
  name: "qw",
  users: [
   { name: "Zorya" },
 ]
}, {
  name: "qw",
  users: [
    { name: "Var" },
    { name: "Var2" },
  ],
}, {
  name: "qw",
  users: [],
}, {
  name: "qw",
  users: [
    { name: "Aorya" },
  ],
}, {
  name: "qw",
  users: [
    { name: "rwerwerwe" },
    { name: "tregdf" },
    { name: "gdfgdf" },
    { name: "Vayetrtertrr2" },
  ]
}, {
  name: "qw",
  users: [
   { name: "Dorya" },
  ],
}];

function orderCustomBy(collection, key, direction) {
  const direct = direction === "desc" ? -1 : 1;
  
  let compare = (a, b) => {
    if (a === null) return -1;
    if (b === null) return 1;
    // Just commenting this out because there's no `intlCollator` in here:
    // return intlCollator.compare(a, b);
  };
  
  if (key === "users") {
    compare = (a, b) => {
      // What should go in here?
      // intlCollator.compare(a[0].name, b[0].name);
      
      return 1;
    };
  }
  
  return [].concat(collection).sort((a, b) => {
    const result = compare(a[key], b[key]);
    return result * direct;
  });
}

console.log(orderCustomBy(array, 'users', 'asc')
  .map(item => item.users.length === 1 ? item.users[0].name : item.users.length));
  
console.log(orderCustomBy(array, 'users', 'desc')
  .map(item => item.users.length === 1 ? item.users[0].name : item.users.length));
.as-console-wrapper {
  max-height: 100% !important;
}


Solution

  • You basically need to account for a few different combinations inside the compare function when key === 'users:

    It will look something like this:

    if (a.users.length === 1 && b.users.length === 1) {
      // If both have a single user, sort by users[0].name:
      return a.users[0].name.localeCompare(b.users[0].name);
    } else if (a.users.length === 1) {
      // If only `a` has a single user, `a` goes before `b`:
      return -1;
    } else if (b.users.length === 1) {
      // If only `b` has a single user, `b` goes before `a`:
      return 1;
    }
    
    // Otherwise, sort by users.length:
    return a.users.length - b.users.length;
    

    Here you can see it in action:

    const array = [{
      name: "qw",
      users: [
        { name: "Borya" },
      ],
    }, {
      name: "qw",
      users: [
        { name: "Gorya" },
      ],
    }, {
      name: "qw",
      users: [
       { name: "Zorya" },
     ]
    }, {
      name: "qw",
      users: [
        { name: "Var" },
        { name: "Var2" },
      ],
    }, {
      name: "qw",
      users: [],
    }, {
      name: "qw",
      users: [
        { name: "Aorya" },
      ],
    }, {
      name: "qw",
      users: [
        { name: "rwerwerwe" },
        { name: "tregdf" },
        { name: "gdfgdf" },
        { name: "Vayetrtertrr2" },
      ]
    }, {
      name: "qw",
      users: [
       { name: "Dorya" },
      ],
    }];
    
    function orderCustomBy(collection, key, direction) {
      const direct = direction === "desc" ? -1 : 1;
      
      let compare;
      
      if (key === "users") {
        compare = (a, b) => {
          if (a.users.length === 1 && b.users.length === 1) {
            // If both have a single user, sort by users[0].name:
            return a.users[0].name.localeCompare(b.users[0].name);
          } else if (a.users.length === 1) {
            // If only `a` has a single user, `a` goes before `b`:
            return -1;
          } else if (b.users.length === 1) {
            // If only `b` has a single user, `b` goes before `a`:
            return 1;
          }
          
          // Otherwise, sort by users.length:
          return a.users.length - b.users.length;
        };
      } else {
        compare = (a, b) => {
          if (a === null) return -1;
          if (b === null) return 1;
          // Just commenting this out because there's no `intlCollator` in here:
          // return intlCollator.compare(a, b);
        };
      }
      
      return [].concat(collection).sort((a, b) => compare(a, b) * direct);
    }
    
    console.log(orderCustomBy(array, 'users', 'asc')
      .map(item => item.users.length === 1 ? item.users[0].name : item.users.length));
      
    console.log(orderCustomBy(array, 'users', 'desc')
      .map(item => item.users.length === 1 ? item.users[0].name : item.users.length));
    .as-console-wrapper {
      max-height: 100% !important;
    }