Search code examples
javascriptjquerysortingasynchronoususer-input

Javascript custom sort function depending on user input


I'm trying to make a custom compare callback function for the Javascript array.sort method. It should return a value depending on user input.

When that callback function is called, it should wait until the user clicks a button. Depending on the clicked button the function would know which of the two elements is bigger and return the corresponding result (1 or -1).

The only way I know to wait for user input is with an event listener function, but I don't know how to adapt this to the custom function I have to pass to the array.sort() method.

Here is the code I'm trying with; I know this code won't work:

var array=["a","b","c"];
array.sort(function(a,b){
    var result;

    $("#button1").click(function(){
        result = 1;
    });

    $("#button2").click(function(){
        result = -1;
    });
    return result;
}

I'm starting to think it's impossible to use the array.sort function for this purpose.

Is there a way to do this?


Solution

  • You could do this the ugly way, by making use of the window.confirm method, which has the behaviour to wait with the further execution of your code until the user closes the pop-up by either choosing OK or Cancel:

    array=["a","b","c"];
    array.sort(function(a,b){
        var a_before_b = confirm("'" + a + "' will be sorted before '" + b 
                               + "'. Cancel to order differently.");
        return a_before_b ? -1 : 1;
    });
    document.body.innerHTML = '<p>Array sorted: ' + array + '</p>';
    <p>You will get 2 to 3 pop up dialogs to sort [a, b, c]</p>

    To have the user answer via normal interaction, not via modal dialogs, and still use the standard sort method, is a harder task.

    One idea would be to keep a list of answers given, and repeat the sort each time an new answer is available, feeding those answers via the sort callback function.

    Once a comparison comes up for which no answer was yet given, you would then present that comparison to the user, and complete the rest of the sort with any order. That sorted array will be ignored, up until the moment you have all answers for all comparisons.

    The code is quite long, mostly because of the interaction with the page:

    var array = ['a', 'b', 'c'];
    var answers = [];
    var questionTemplate = "Order '#' before or after '#'?"; 
    var answerTemplate = "The array is sorted. Result: #."; 
    
    function logMsg(msg) {
        $($('#log')[0].insertRow(-1).insertCell(-1)).text(msg);
    }
    
    function answer(order) {
        // keep track of answers
        answers.push(order);
        // show answers also in a log table
        logMsg($('#question').text() + ' - ' + (order<0? 'Before' : 'After'));
        askNext();
    }
    
    function askNext() {
        var arrayCopy = array.slice(0); // take real copy
        var questionNo = -1;
        arrayCopy.sort(function(a,b){
            questionNo++
            if (questionNo < answers.length) {
                // we already asked this, so use that answer
                return answers[questionNo];
            } 
            if (questionNo == answers.length) {
                // put question to user:
                $('#question').text(questionTemplate.replace('#', a).replace('#', b));
            }
            // don't care for now, just finish it. We need first to
            // get an answer, and will then restart the sort.
            return -1; 
        });
        if (array.length == answers.length) {
            // Array is now sorted according to answers:
            // Hide question section
            $('#questionDiv').hide(); 
            // Show the result
            logMsg(answerTemplate.replace('#', arrayCopy)); 
        }
    }
    
    function reset() {
        $('#array').text(array); 
        $('#questionDiv').show() 
        $('#log').html(''); // empty log table 
        answers = [];
        askNext();
    }
    
    // Set up click handlers
    $('#reset').click(reset);
    $('#before').click(answer.bind(null, -1));
    $('#after').click(answer.bind(null,  1));
    reset();
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <p>The array <span id="array"></span> will be sorted by asking you questions:</p>
    <table id="log"></table>
    <div id="questionDiv">
        <p id="question" style="font-weight: bold"></p> 
        <button id="before">Before</button><button id="after">After</button>
    </div>
    <button id="reset">Restart</button>