Как отсортировать массив объектов по свойствам слева направо, сначала имея числа, когда свойство является строкой? - PullRequest
3 голосов
/ 23 января 2020

Я пытаюсь отсортировать этот массив сначала по хромосоме, а затем по startPosition, а затем по endPosition. Проблема заключается в том, что свойства имеют строковый тип, а хромосома содержит числа между [1-22] или [x, y, m] * 1001. *

Я создал эту функцию на основе некоторых ответов StackOverflow: он сравнивает значение и сортирует их таким образом, но теперь проблема заключается в том, что это строка с сортировкой, поэтому она ставит 10 перед 2

    const sortByThenBy = (value, sortSettings) => {
    const sortIt = () => {
        const sortOrderBy = (left, right, sortOrder) => {
            const sortMultiplier = sortOrder === 'asc' ? 1 : -1;
            if (left > right) {
                return +1 * sortMultiplier;
            }
            if (left < right) {
                return -1 * sortMultiplier;
            }
            return 0;
        };

        return (sortLeft, sortRight) => {
            const getValueByStr = (obj, path) => {
                let i;
                path = path.replace('[', '.');
                path = path.replace(']', '');
                path = path.split('.');
                for (i = 0; i < path.length; i++) {
                    if (!obj || typeof obj !== 'object') {
                        return obj;
                    }
                    obj = obj[path[i]];
                }
                return obj;
            };

            return sortSettings
                .map(property =>
                    sortOrderBy(
                        getValueByStr(sortLeft, property.prop),
                        getValueByStr(sortRight, property.prop),
                        property.sortOrder
                    )
                )
                .reduceRight((left, right) => right || left);
        };
    };

    return value.sort(sortIt());
};
console.log(
    sortByThenBy(item, [
        {
            prop: 'chromosome',
            numericSort: true,
            sortOrder: 'asc'
        },
        {
            prop: 'startPosition',
            sortOrder: 'asc'
        },
        {
            prop: 'endPosition',
            sortOrder: 'asc'
        }
    ])
);

https://jsbin.com/kumavupuqi/edit?js, консоль, выход

Дано

    const item = [
    { chromosome: '2', startPosition: '980000', endPosition: '989000' },
    { chromosome: '2', startPosition: '978000', endPosition: '979000' },
    { chromosome: '1', startPosition: '978000', endPosition: '979000' },
    { chromosome: '10', startPosition: '978000', endPosition: '979000' },
    { chromosome: 'x', startPosition: '978000', endPosition: '979000' }
];

Ожидаемый результат

const item = [
{ chromosome: '1', startPosition: '978000', endPosition: '979000' },
{ chromosome: '2', startPosition: '978000', endPosition: '979000' },
{ chromosome: '2', startPosition: '980000', endPosition: '989000' },
{ chromosome: '10', startPosition: '978000', endPosition: '979000' },
{ chromosome: 'x', startPosition: '978000', endPosition: '979000' }
];

Ответы [ 2 ]

3 голосов
/ 23 января 2020

В обратном вызове .sort сортировка по:

(1) Разница между хромосомами двух элементов составляет 'y'

(2) Разница между хромосомами двух элементов составляет 'x'

(3) Числовая c разница между хромосомами двух элементов

(4) Числовая c разница между начальной точкой двух элементовПозиция

( 5) Числовая c разница между конечной позицией двух элементов

, где возвращается первая разница, которая не ноль:

const arr = [
  { chromosome: '2', startPosition: '980000', endPosition: '989000' },
  { chromosome: '2', startPosition: '978000', endPosition: '979000' },
  { chromosome: '1', startPosition: '978000', endPosition: '979000' },
  { chromosome: '10', startPosition: '978000', endPosition: '979000' },
  { chromosome: 'x', startPosition: '978000', endPosition: '979000' },
  { chromosome: 'x', startPosition: '5', endPosition: '979000' },
  { chromosome: '1', startPosition: '978000', endPosition: '9999999' },
];

arr.sort((a, b) => (
  ((a.chromosome === 'y') - (b.chromosome === 'y')) ||
  ((a.chromosome === 'x') - (b.chromosome === 'x')) ||
  (a.chromosome - b.chromosome) ||
  (a.startPosition - b.startPosition) ||
  (a.endPosition - b.endPosition)
));
console.log(arr);

Это сделано очень кратким из-за того, что - неявно приводит обе стороны к числам.

Другой вариант - создать функцию, которая, учитывая один объект, может придумать относительное значение для этого объекта (например, значение каждой разницы хромосом от 0 в 1e20, каждой разницы startPosition от 0 в 1e10 и каждой разницы endPosition от 0 в 1). Затем пропустите оба объекта через эту функцию и проверьте разницу между их относительными значениями:

const arr = [
  { chromosome: '2', startPosition: '980000', endPosition: '989000' },
  { chromosome: '2', startPosition: '978000', endPosition: '979000' },
  { chromosome: '1', startPosition: '978000', endPosition: '979000' },
  { chromosome: '10', startPosition: '978000', endPosition: '979000' },
  { chromosome: 'x', startPosition: '978000', endPosition: '979000' },
  { chromosome: 'x', startPosition: '5', endPosition: '979000' },
  { chromosome: '1', startPosition: '978000', endPosition: '9999999' },
];

const getVal = (obj) => {
  const chr = obj.chromosome;
  const chromVal = chr === 'y'
    ? 25
    : chr === 'x'
      ? 24
      : Number(chr);
  return (
    chromVal * 1e20 +
    obj.startPosition * 1e10 +
    Number(obj.endPosition)
  );
  return totalVal;
};

arr.sort((a, b) => getVal(a) - getVal(b));
console.log(arr);
2 голосов
/ 23 января 2020

Это может помочь.

Количество цифр, необходимых для координат хромосомы, варьируется. Я использовал информацию отсюда: https://www.biostars.org/p/4355/, чтобы угадать разумное значение для POSITION_DIGITS.

Этот код использует сравнения строк. JavaScript не может точно представлять произвольно большие целые числа.

const item = [{
    chromosome: '2',
    startPosition: '980000',
    endPosition: '989000'
}, {
    chromosome: '2',
    startPosition: '40',
    endPosition: '60'
}, {
    chromosome: '1',
    startPosition: '978000',
    endPosition: '979000'
}, {
    chromosome: '10',
    startPosition: '978000',
    endPosition: '979000'
}, {
    chromosome: 'x',
    startPosition: '978000',
    endPosition: '979000'
}];

const LETTERS = { x: '23', y: '24', m: '25' }
const CHROMOSOME_DIGITS = 2
const POSITION_DIGITS = 10

const toStr = ({chromosome, startPosition, endPosition}) => 
    ((LETTERS[chromosome] || chromosome.padStart(CHROMOSOME_DIGITS,'0')) + 
    startPosition.padStart(POSITION_DIGITS,'0') + 
    endPosition.padStart(POSITION_DIGITS,'0'))

console.log(item.sort((a,b) => toStr(a).localeCompare(toStr(b))))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...