BluePrint
BluePrint

Reputation: 2134

JavaScript: Sorting array accorting to "outside" parameter

In JavaScript, I have an array of objects and an input value like this:

const inputVal = 'foo';

const objArray = [
    {title: 'blahaa', type: 'foobar'},
    {title: 'foobar', type: 'wohoo'},
    {title: 'foobar', type: 'foobar'},
    {title: 'foobar', type: 'aaaabaa'},
    {title: 'foobar', type: 'moo'},
    {title: 'aaah', type: 'foogoo'},
    {title: 'foohiii', type: 'foobar'},
    {title: 'aaah', type: 'foobar'},
    {title: 'foodoo', type: 'aaaabaa'},
    {title: 'gaaaaaah', type: 'foobar'},
    {title: 'foobar', type: 'foogoo'},
];

As you can see, all elements in the array have properties starting with "foo" in either the title or the type. Also, all elements are unique, the same title and/or type can be in several elements, but the same combination of both cannot appear twice.

I want to sort this array in the following way:

  1. Both title and type starts with inputVal
    1. Type alphabetically
    2. Title alphabetically
  2. Title starts with inputVal but not type
    1. Type alphabetically
    2. Title alphabetically
  3. Type starts with inputVal but not title
    1. Type alphabetically
    2. Title alphabetically

The example list would be sorted as following:

const objArray = [
    {title: 'foobar', type: 'foobar'}, // Criterium 1
    {title: 'foohiii', type: 'foobar'}, // Criterium 1
    {title: 'foobar', type: 'foogoo'}, // Criterium 1
    {title: 'foobar', type: 'aaaabaa'}, // Criterium 2
    {title: 'foodoo', type: 'aaaabaa'}, // Criterium 2
    {title: 'foobar', type: 'moo'}, // Criterium 2
    {title: 'foobar', type: 'wohoo'}, // Criterium 2
    {title: 'aaah', type: 'foobar'}, // Criterium 3
    {title: 'blahaa', type: 'foobar'}, // Criterium 3
    {title: 'gaaaaaah', type: 'foobar'}, // Criterium 3
    {title: 'aaah', type: 'foogoo'}, // Criterium 3
];

I tried using array.prototype.sort(callback) with several different callback functions, but I don't seem to get the correct one. Can anyone help me?

Upvotes: 0

Views: 68

Answers (3)

Sackey
Sackey

Reputation: 402

Here is another solution, probably less efficient due to the use of multiple regex.

const objArray = [
    {title: 'blahaa', type: 'foobar'},
    {title: 'foobar', type: 'wohoo'},
    {title: 'foobar', type: 'foobar'},
    {title: 'foobar', type: 'aaaabaa'},
    {title: 'foobar', type: 'moo'},
    {title: 'aaah', type: 'foogoo'},
    {title: 'foohiii', type: 'foobar'},
    {title: 'aaah', type: 'foobar'},
    {title: 'foodoo', type: 'aaaabaa'},
    {title: 'gaaaaaah', type: 'foobar'},
    {title: 'foobar', type: 'foogoo'}
];

const input = "foo";

const sortedObjArray = [
    objArray.sort((a, b) => {
        const
            regex = new RegExp("^(" + input + ")?(.*)", "i"),
            titleAExec = regex.exec(a.title),
            typeAExec = regex.exec(a.type),
            titleBExec = regex.exec(b.title),
            typeBExec = regex.exec(b.type)

        const
            titleComparison = titleAExec[2].localeCompare(titleBExec[2]),
            typeComparison = typeAExec[2].localeCompare(typeBExec[2])

        if (titleAExec[1] && !titleBExec[1])
            return -1
        else if (titleBExec[1] && !titleAExec[1])
            return 1

        if (typeAExec[1] && !typeBExec[1])
            return -1
        else if (typeBExec[1] && !typeAExec[1])
            return 1

        if (typeComparison === 0)
            return titleComparison

        return typeComparison;
    })
]

console.log(sortedObjArray)

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386883

You could chanin the criteria and sort by the three groups first and then by the values.

const
    value = 'foo',
    start = (v => s => s.startsWith(v))(value),
    array = [{ title: 'blahaa', type: 'foobar' }, { title: 'foobar', type: 'wohoo' }, { title: 'foobar', type: 'foobar' }, { title: 'foobar', type: 'aaaabaa' }, { title: 'foobar', type: 'moo' }, { title: 'aaah', type: 'foogoo' }, { title: 'foohiii', type: 'foobar' }, { title: 'aaah', type: 'foobar' }, { title: 'foodoo', type: 'aaaabaa' }, { title: 'gaaaaaah', type: 'foobar' }, { title: 'foobar', type: 'foogoo' }];
    
array.sort((a, b) =>
       (start(b.title) && start(b.type)) - (start(a.title) && start(a.title))
    || start(b.title) - start(a.title)
    || start(b.type) - start(a.type)
    || a.type.localeCompare(b.type)
    || a.title.localeCompare(b.title)
);

console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 3

Baljinder Singh
Baljinder Singh

Reputation: 1

You can try the following:

  1. Split array in 3 subarrays using Array.prototype.filter based on 3 Criterias as mentioned above.
  2. Sort 3 subarrays independently and then concatenate using Array.prototype.concat

To improve performance, process all subarrays asynchronously. Divide and Conquer wins!

Upvotes: -1

Related Questions