sumit
sumit

Reputation: 15464

sorting when keys are not present javascript using localecompare

I have json like below

[
{name:'aa',age:'1Y',address:'Alaska'},
{name:'cc',age:'4Years',address:'California'},
{name:'mm',address:'Texas'}
]

Whenever I sort with name and address it work but it will throw runtime error if I try to sort with age as it is missing on last entry.

This is my attempt

let obj=[
{name:'aa',age:'2y',address:'Alaska'},
{name:'cc',age:'4y',address:'California'},
{name:'bb',address:'Texas'}
]
let field='age'; //work for name and address;
let mode='string';
if(mode!='number'){
console.log (obj.sort((a, b) => a[field].toString().localeCompare(b[field].toString())));
}
else{
console.log(obj.sort((a, b) => a[field] -b[field]))
}

What is the best way to ignore the entry when keys are not present , do I need to have seperate loop to check keys before sorting . Entry with missing keys will be on the bottom.

Ps: Ages are never >10 years from the business logic and they can come in any format like 1,1Y so it is treated as string

Upvotes: 0

Views: 312

Answers (5)

Ele
Ele

Reputation: 33726

Entry with missing keys will be on the bottom

Ask for the current value to decide what will be compared or what will be at the bottom.

let obj=[{name:'aa',age:'2y',address:'Alaska'},{name:'cc',age:'4y',address:'California'},{name:'bb',address:'Texas'}],
    field = 'age';
    
console.log(obj.sort((a, b) => a[field] ? b[field] ? a[field].toString().localeCompare(b[field].toString()) : -1 : 1));
.as-console-wrapper { max-height: 100% !important; top: 0; }

If you want to compare the numbers within this string 10years or this string 5y, and so on, use a regex to compare the numbers.

let obj=[{name:'aa',age:'24y',address:'Alaska'},{name:'cc',age:'4years',address:'California'},{name:'bb',address:'Texas'}],
    field = 'age';
    
console.log(obj.sort((a, b) => {
  let evaluate = () => {
    let aval = a[field].replace(/[^\d]/g, '').trim();
    let bval = b[field].replace(/[^\d]/g, '').trim();
    
    return aval !== '' && bval !== ''  ? Number(aval) - Number(bval) : a[field].toString().localeCompare(b[field].toString());
  };
  
  return a[field] ? b[field] ? evaluate() : -1 : 1
}));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 1

kshetline
kshetline

Reputation: 13700

Just in case you're looking for an overkill solution :)

  let obj=[
    {name: 'aa', age: 25, address: 'Alaska'},
    {name: 'cc', age: 24, address: 'California'},
    {name: 'mm', address: 'Texas'}
  ];

  let field = 'age';
  console.log (obj.sort((a, b) => {
    a = a[field];
    b = b[field];
    let defaultValue = '';

    if (typeof a === 'number' || typeof b === 'number') {
      defaultValue = 0;
    }

    a = a || defaultValue;
    b = b || defaultValue;

    if (typeof a === 'number') {
      return a - b;
    }
    else {
      return a.localeCompare(b);
    }
  }));

This automatically handles either strings or numbers, sorting correctly for each. If you want the no-age entries to sort higher rather than lower than everything else, just change what defaultValue is set to for numbers to a large number.

Upvotes: 0

Icepickle
Icepickle

Reputation: 12806

Just make sure you either have the value of the object, or return an empty string.

The shortest code path would be

(a[field] || "")

Where you indicate that if a doesn't have the property, it will treat it as an empty string.

It won't cover for a being null though, so if that can happen, you have to check it more carefully still

let obj = [{
    name: 'aa',
    age: '25',
    address: 'Alaska'
  },
  {
    name: 'cc',
    age: '25',
    address: 'California'
  },
  {
    name: 'bb',
    address: 'Texas'
  }
]
let field = 'age'; //work for name and address
console.log(obj.sort((a, b) => (a[field] || "").toString().localeCompare((b[field] || "").toString())));

Another way to do this, would be to simply compare the values (note, again, if a or b would be null, there might be a problem)

let obj = [{
    name: 'aa',
    age: 25,
    address: 'Alaska'
  },
  {
    name: 'cc',
    age: 3,
    address: 'California'
  },
  {
    name: 'bb',
    address: 'Texas'
  }
]

function sortAndPrint( obj, field ) {
  console.log(`Sorting by ${field}`);
  console.log(obj.sort((a, b) => a[field] > b[field] ) );
}

sortAndPrint(obj, 'name');
sortAndPrint(obj, 'address');
sortAndPrint(obj, 'age');

Upvotes: 4

Gabriel Terry
Gabriel Terry

Reputation: 53

This happens because on the last element you don't have the property age when you're trying to access it with property toString.(it is null with the age key)

Upvotes: 0

Kevin Qian
Kevin Qian

Reputation: 2720

let obj=[
  {name:'aa',age:'25',address:'Alaska'},
  {name:'cc',age:'25',address:'California'},
  {name:'bb',address:'Texas'}
]
let field='age'; //work for name and address
const sortFunc = (a, b) => a[field].toString().localeCompare(b[field].toString())

// If you can discard entries without the field
console.log(obj.filter(e => field in e).sort(sortFunc))

// If you cannot
console.log(obj.filter(e => field in e).sort(sortFunc).concat(obj.filter(e => !(field in e))))

Upvotes: 0

Related Questions