0

I have an input element and when the user types on it my code searches through an array for the input value and prints its element if it exists and it works fine. The problem is when I'm printing it I want to sort the final array alphabetically but also I want the searched strings index value position matches to come up first, alphabetically, then the rest after them. To explain this better say my array is ['testB', 'testD', 'testC', 'dtest', 'cTest'] and the user searched for test I want the final response array to be ["testB","testC","testD", "cTest", "dtest"] is kind of sorted but it gives priority to same index positioned characters. How can I achieve this?

const myArray = ['testB', 'testD', 'testC', 'dtest', 'cTest']
let tempArray = []

$('input').on('input', function() {
  tempArray = []
  for (var i = 0; i < myArray.length; i++) {
    if (myArray[i].toLowerCase().includes($(this).val().toLowerCase())) {
      tempArray.push(myArray[i])
    }
  }
  //I want it to write ["testB","testC","testD", "cTest", "dtest"]
  $('p').html(JSON.stringify(tempArray.sort()))
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input />
<p></p>
Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
seriously
  • 1,202
  • 5
  • 23

2 Answers2

1

you can do something like this

const myArray = ['testB', 'testD', 'testC', 'dtest', 'cTest']
let tempArray = []

$('input').on('input', function() {
  
  const val = $(this).val().toLowerCase()
  tempArray = myArray.filter(a => a.toLowerCase().includes(val))
  //I want it to write ["testB","testC","testD", "cTest", "dtest"]
  const startsWith = (string, value) => string.substring(0, value.length).toLowerCase() === value
 
  $('p').html(JSON.stringify(tempArray.sort((a, b) => {
      const aStartsWith = startsWith(a, val)
      const bStartsWith = startsWith(b, val)

      if( aStartsWith && !bStartsWith){
        return -1
      }else if (!aStartsWith && bStartsWith){
        return 1;
      }
     
      return b > a?-1:1
  })))
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input />
<p></p>

edit Tried with the array @seriously said that fails but it seems to work fine

const myArray = ['the big bang theory', 'the office', 'top gun 2 maverick', 'the man from toronto', 'the bad guys']
let tempArray = []

$('input').on('input', function() {
  
  const val = $(this).val().toLowerCase()
  tempArray = myArray.filter(a => a.toLowerCase().includes(val))
  //I want it to write ["testB","testC","testD", "cTest", "dtest"]
  const startsWith = (string, value) => string.substring(0, value.length).toLowerCase() === value
 
  $('p').html(JSON.stringify(tempArray.sort((a, b) => {
      const aStartsWith = startsWith(a, val)
      const bStartsWith = startsWith(b, val)

      if( aStartsWith && !bStartsWith){
        return -1
      }else if (!aStartsWith && bStartsWith){
        return 1;
      }
     
      return b > a?-1:1
  })))
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input />
<p></p>
R4ncid
  • 6,944
  • 1
  • 4
  • 18
  • Remember that, as I quote from the [docs of `Array.prototype.sort()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#description), “A comparator conforming to the constraints above will always be able to return all of 1, 0, and -1, or consistently return 0.”. So, for completeness, you should convert the last ternary operator into `if`s. – CcmU Jul 19 '22 at 14:53
  • 1
    @CcmU you're right but is pretty pointless in this case given that if the the string are equals it does't matter which come first – R4ncid Jul 19 '22 at 15:04
  • 1
    This fails!! Try setting ```myArray = ['the big bang theory', 'the office', 'top gun 2 maverick', 'the man from toronto', 'the bad guys']``` – seriously Jul 19 '22 at 17:00
  • @seriously what do you mean with it fails? I tried to change with the array as you said and it seems to work fine – R4ncid Jul 20 '22 at 06:46
  • @R4ncid no it doesn't the sorted array returns ```['the bad guys', 'the man from toronto', 'the big bang theory', 'the office', 'top gun 2 maverick' ]``` – seriously Jul 20 '22 at 07:54
  • @seriously I see `["the bad guys","the big bang theory","the man from toronto","the office","top gun 2 maverick"]` as expected. have you tried the code snippets above? – R4ncid Jul 20 '22 at 09:45
0

The key point of sort is compare function. For element a and b, which one has higher priority? There are three principe as your description:

  1. element contains search string has higher priority then not;
  2. if both element contain the search string, the lower index has higher priority;
  3. if index of search string is the same, then compare the two string element, with > operator.

If only want items contain the search value, you can add a filter before sort.

var search = 'test';

function compare(a, b) {
  var indexA = a.indexOf(search);
  var indexB = b.indexOf(search);
  
  if (indexA === indexB) {
    return a > b ? 1 : -1;
  }
  if (indexA === -1) {
    return 1;
  }
  if (indexB === -1) {
    return -1;
  }
  return indexA > indexB ? 1 : -1;
}

var arr = ['testB','AString', 'somthing', 'testC', 'testD', 'test1', 'Ctest', 'Atest', 'ADtest', 'ABtest'];

// filter search value before sort
function searchResult(arr) {
  return arr.filter(item => item.includes(search)).sort(compare);
}

console.log('result:', arr.sort(compare));
console.log('searchResult:', searchResult(arr));
BigLiao
  • 507
  • 3
  • 9
  • if your set ```var search = 'a';``` and run your function the return array will have items that don't even include the char ```a``` – seriously Jul 19 '22 at 16:37
  • @seriously If only want items contains search value, you can add a `filter` before `sort`. Updated my answer. – BigLiao Jul 20 '22 at 01:49