Как отсортировать массив по типу элемента, где строки должны отображаться 1-м, а затем плавающими и целыми числами, сохраняя приоритет того же типа и не используя дополнительные массивы

#javascript #arrays #performance #sorting #comparison

Вопрос:

Ввод:

 [7,"a","b",5, 0.1, "c", 0.5, 9, 1, "e", "m", 0.3, 8.5, 74, 89,"f","r",0.5 ,"x", "y", 4, 7]
 

Выход:

 ["a", "b", "c", "e", "m", "f", "r", "x", "y", 0.1, 0.5, 0.3, 8.5, 0.5, 7, 5, 9, 1, 74, 89, 4, 7]
 
 
function sorter(arr) {
    var arr1, arr2, arr3;
    arr1 = [];
    arr2 = [];
    arr3 = [];
    for (var i = 0; i < arr.length; i  ) {
        if (typeof arr[i] === "string") {
            arr1.push(arr[i]);
        } else if (typeof arr[i] === 'number' amp;amp; !isNaN(arr[i])) {
            if (Number.isInteger(arr[i])) {
                arr3.push(arr[i]);
            } else {
                arr2.push(arr[i]);
            }
        }
    }
    return [...arr1, ...arr2, ...arr3];
}

 

Комментарии:

1. Вы хотите отредактировать функцию компаратора сортировки? Что происходит, когда вы запускаете свой текущий код

2. Использование массивов смешанного типа, подобных этому, вероятно, является проблемой xy для начала. Вы уверены , что вам нужны данные в этой форме, и нет лучшего представления? До тех пор, пока вы сохраняете данные в этой форме, ваши алгоритмы для работы с ними будут неудобными, с множеством ветвей для проверки типов повсюду.

3. Я добавил выходные данные для своего текущего кода. Просто я хочу оптимизировать свою функцию сортировки, не используя никаких дополнительных массивов.

4. Сначала в JS нет смысла указывать «целые числа», потому что в JS нет целых чисел. «12.0» и «12» — это точно одно и то же число, так что бы произошло, если бы ваш ввод был [11, 12.0, 12] ?

5. @Анкита … Существует бережливый подход, основанный на обратном вызове одного компаратора сортировки

Ответ №1:

Используйте sort() метод с функцией сравнения, которая сравнивает типы, а не значения.

 function sorter(arr) {
  function type_index(val) {
    const types = ["string", "float", "integer"];
    let type;
    if (typeof val == "string") {
      type = "string";
    } else if (typeof val == "number" amp;amp; Number.isInteger(val)) {
      type = "integer";
    } else if (typeof val == "number" amp;amp; !isNaN(val)) {
      type = "float";
    }
    return types.indexOf(type);
  }

  return arr.sort((a, b) => type_index(a) - type_index(b));
}

console.log(sorter([7,"a","b",5, 0.1, "c", 0.5, 9, 1, "e", "m", 0.3, 8.5, 74, 89,"f","r",0.5 ,"x", "y", 4, 7])); 

Ответ №2:

Подход, основанный на одном sort обратном вызове компаратора …

 function orderByItemTypeOnly(a, b) {
  const precedences = {
    'string': 1,
    'float': 2,
    'int': 3,
  };
  const getType = val =>
    ((typeof val === 'number') amp;amp; Number.isFinite(val))
      ? Number.isInteger(val) amp;amp; 'int' || 'float'
      : (typeof val);

  const aType = getType(a);
  const bType = getType(b);

  return (
    (precedences[aType] || 4) - (precedences[bType] || 4)
  );
}

console.log([

  7, "a", "b", 5, 0.1, "c", 0.5, 9, 1, "e", "m",
  0.3, 8.5, 74, 89, "f", "r", 0.5, "x", "y", 4, 7

].sort(orderByItemTypeOnly)); 
 .as-console-wrapper { min-height: 100%!important; top: 0; } 

Существует также возможность решения этой задачи не с помощью sort , а на основе reduce подхода …

 function collectAndShiftItemByType(list, item, idx, arr) {
  if (list.length === 0) {
    list.push([], [], [], []);
  }
  const typeListIndex = ((typeof item === 'number') amp;amp; Number.isFinite(item))
    ? (Number.isInteger(item) amp;amp; 2 || 1)
    : (typeof item === 'string') ? 0 : 3;

  // push into sub lists ... [0]string, [1]float, [2]integer, [3]unknown.
  list[typeListIndex].push(item);

  if (idx >= arr.length - 1) {
    list = list[0]
      .concat(list[1])
      .concat(list[2])
      .concat(list[3]);
  }
  return list;
}

console.log([

  7, "a", "b", 5, 0.1, "c", 0.5, 9, 1, "e", "m",
  0.3, 8.5, 74, 89, "f", "r", 0.5, "x", "y", 4, 7

].reduce(collectAndShiftItemByType, [])); 
 .as-console-wrapper { min-height: 100%!important; top: 0; }