0

I have an array of objects like so :

[
  {key: "key1", label: "1"},
  {key: "key2", label: "3"},
  {key: "key3", label: "2"}, 
  {key: "key4", label: "Second"}, 
  {key: "key5", label: "First"}
] 

I would like to sort this array so the alphabetical values come first like so :

[
  {key: "key5", label: "First"},
  {key: "key4", label: "Second"}, 
  {key: "key1", label: "1"},
  {key: "key3", label: "2"}, 
  {key: "key2", label: "3"}    
] 

I came up with this solution:

sortArray(list: any[], key: string) {
  return list.sort(compare);

  function compare(a, b) {
    const aIsAlphabetic = isAlphabetical(a[key]);
    const bIsAlphabetic = isAlphabetical(b[key]);

    if (aIsAlphabetic && !bIsAlphabetic) {
      return -1;
    } else if (bIsAlphabetic && !aIsAlphabetic) {
      return 1;
    }

    if (a[key] < b[key]) {
      return -1;
    }
    if (a[key] > b[key]) {
      return 1;
    }
    return 0;
  }

  function isAlphabetical(value: string) {
    return value.match(/[a-zA-Z]/i);
  }
}

The solution works, but I don't like the idea of declaring several functions inside of sortArray, am I doing it right? Or are there any other tips to make properly?

2
  • Did U tried to sort only by label and then alphabet signs order first? U could first divide entries to this with alphanumeric, sort them, then add rest of entries to result list. Commented Jan 20, 2020 at 14:37
  • I recommend guarding against undefined key values. Commented Jan 20, 2020 at 18:10

3 Answers 3

1

This is quite an opinion based question, especially with javascript/typescript involved!

The short answer is that there is no correct answer. Your code, as you have said, works! It is also clean, readable and concise.

The downside of writing nested functional code like this is that it is not possible to unit test each function in isolation. If you wanted to properly test this function as a whole, you will have to pump a large number of test cases through it to make sure its doing its job.

Personally i would simply move those nested functions into a high level scope so that i could test them individually.

Sign up to request clarification or add additional context in comments.

1 Comment

I would not test that individually. You should be testing the function... not testing HOW it does that. The function is a black box and nothing should care, nor know, about implementation details. Should someone at a later date rewrite the function so have the exact same end results, the test structure may fail if "private" functions change or are removed.
1

There's nothing wrong with your solution. If you may be writing similar sort functions you could use a generic function, passing match functions.

const sortArray = (arr, key, rgx, bothMatched, neitherMatched, onlyAMatched, onlyBMatched) => {
  const compare = (a, b) => {   
    const aval = a[key] || "";
    const bval = b[key] || "";
    const amatched = aval.match(rgx);
    const bmatched = bval.match(rgx);   
    if (amatched) {
      if (bmatched) {     
        return bothMatched(aval,bval);
      }    
      return onlyAMatched(a,b);
    }
    if (bmatched) {   
      return onlyBMatched(a,b)
    }     
    return neitherMatched(aval,bval);
  };  

  return arr.sort(compare);
};
 
const arr = [
  {key: "key1", label: "1"},
  {key: "key2", label: "3"},
  {key: "key3", label: "2"}, 
  {key: "key4", label: "Second"}, 
  {key: "key5", label: "First"}
] 
const key = 'label';

console.log("----- match like OP -----");
sortArray(
  arr, 
  key, 
  /[a-zA-Z]/i,
  (a,b) => a.localeCompare(b),
  (a,b) => a.localeCompare(b), 
  () => -1,
  () => 1)
.forEach(e => console.log(JSON.stringify(e)));

console.log("----- Alternative match -----");
sortArray(
  arr, 
  key, 
  /^[\d]+$/,
  (a,b) => parseInt(a,10) - parseInt(b,10), 
  (a,b) => a.localeCompare(b),
  () => 1,
  () => -1)
.forEach(e => console.log(JSON.stringify(e)));
console.log(".");

Comments

1

This should be enough. Still pattern 'a-zA-Z' is not safe for other languages.

let array=[{key: "key1", label: "1"}, {key: "key2", label: "3"}, {key: "key3", label: "2"}, {key: "key4", label: "Second"},{key: "key5", label: "First"}]
let isAlphanumeric = (value:string) => (value.match(/[a-zA-Z]/i))

console.log(array.sort((a, b) => a.label.localeCompare(b.label))
.filter(obj => isAlphanumeric(obj.label))
.concat(array.filter(obj => !isAlphanumeric(obj.label))))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.