Эффективный человеко-читаемый алгоритм сортировки строк даты в Javascript - PullRequest
0 голосов
/ 09 июня 2019

Я пытаюсь отсортировать тысячи строк дат из моей базы данных в моей таблице React. Компонент, который я использую, позволяет пользователям сортировать информацию, нажимая на верхнюю строку, API - это здесь .

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

Однако оказывается, что этот процесс является трудоемким и вызывает задержку функции сортировки при запуске в браузере. Соответствующий фрагмент кода выглядит следующим образом:

let isValidDate = (date) => moment(date,'MMM D, YYYY hh:mm a', true).isValid();

function sort(a, b) { // Sorting algorithm 
    if(isValidDate(a) && isValidDate(b)){
      return 
      if(moment(b).valueOf() < moment(a).valueOf()){
        return -1;
      }
      if(moment(b).valueOf() > moment(a).valueOf()){
        return 1;
      }
    }
    return 0;
  }

Из-за того, как моя таблица отображает даты (в виде удобочитаемых строк), мне нужен какой-то способ сортировки строк по их раз более эффективно. У кого-нибудь есть предложения о том, как этого добиться? Я сортирую тысячи значений, и это решение слишком медленное.

Спасибо!

Ответы [ 2 ]

1 голос
/ 09 июня 2019

Я предполагаю, что эти строки являются частью ваших данных о состоянии. Вместо того, чтобы просто хранить строки (если это то, что вы делаете), я бы сохранял объекты со значением даты в виде числа (миллисекунды с начала эпохи) и отдельно читаемой человеком строки. Тогда обратный вызов сортировки может использовать номер напрямую, вместо того, чтобы постоянно пересматривать строки.

Live Пример:

class Example extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            list: props.list.map(strValue => ({
                dateValue: moment(strValue, 'MMM D, YYYY hh:mm a').valueOf() || 0, // `|| 0` because `valueOf` will return NaN if the date is invalid; we substitute 0 instead
                strValue
            }))
        };
        this.sortAscending  = this.sortAscending.bind(this);
        this.sortDescending = this.sortDescending.bind(this);
    }
    sortAscending() {
        this.setState(({list}) => ({
            list: list.slice().sort((a, b) => a.dateValue - b.dateValue)
        }));
    }
    sortDescending() {
        this.setState(({list}) => ({
            list: list.slice().sort((a, b) => b.dateValue - a.dateValue)
        }));
    }
    render() {
        const {list} = this.state;
        return (
            <React.Fragment>
                <input type="button" onClick={this.sortAscending} value="Sort Ascending" />
                <input type="button" onClick={this.sortDescending} value="Sort Descending" />
                <table>
                    <tbody>
                        {list.map(({strValue}, index) => (
                            <tr key={index}>
                                <td>{strValue}</td>
                            </tr>
                        ))}
                    </tbody>
                </table>
            </React.Fragment>
        );
    }
}

const list = [
    "Jan 2, 2017 12:55 am",
    "Nov 17, 2012 09:06 am",
    "May 19, 2015 05:20 am",
    "May 3, 2015 12:30 pm",
    "Nov 13, 2011 01:44 pm",
    "Jun 25, 2012 03:23 am",
    "Dec 12, 2017 07:42 pm",
    "Dec 12, 2011 04:51 am",
    "Oct 31, 2016 05:56 am",
    "Jan 11, 2013 12:17 pm",
    "Jun 20, 2018 07:05 pm",
    "Mar 21, 2013 04:23 am",
    "Oct 8, 2010 06:37 pm",
    "Jun 23, 2011 04:00 am",
    "Feb 16, 2016 08:57 am",
    "Jul 10, 2016 10:03 pm",
    "Nov 16, 2015 12:17 pm",
    "Jul 7, 2013 05:53 am",
    "Oct 11, 2016 11:52 pm",
    "May 20, 2015 01:00 pm",
    "Oct 4, 2016 10:06 am",
    "Aug 27, 2015 06:16 am",
    "Feb 24, 2013 07:39 am",
    "Jul 28, 2018 10:06 pm",
    "Nov 2, 2018 03:24 am",
    "Apr 23, 2016 09:36 pm",
    "Apr 11, 2010 05:21 am",
    "May 16, 2014 07:52 pm",
    "Sep 17, 2011 05:25 pm",
    "Sep 25, 2010 11:55 am",
    "Jan 30, 2016 01:43 am",
    "Sep 20, 2017 08:23 pm",
    "Feb 6, 2010 01:41 pm",
    "Jul 1, 2009 07:05 am",
    "Jul 12, 2013 03:42 pm",
    "Jun 21, 2011 02:57 am",
    "Jan 16, 2015 05:55 pm",
    "Mar 11, 2012 05:19 pm",
    "Dec 22, 2013 06:34 pm",
    "Oct 29, 2012 07:20 am",
    "May 26, 2015 04:00 am",
    "Feb 21, 2017 09:34 am",
    "Mar 31, 2015 03:37 pm",
    "Jan 24, 2011 11:31 pm",
    "May 28, 2017 03:45 am",
    "Jan 19, 2010 06:45 pm",
    "Jul 31, 2016 03:29 am",
    "May 27, 2011 12:12 am",
    "Dec 7, 2014 11:27 pm",
    "Oct 10, 2016 02:48 am"
];
ReactDOM.render(
    <Example list={list} />,
    document.getElementById("root")
);
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
0 голосов
/ 09 июня 2019

TJ ответ работает. Я закончил тем, что изменил свой бэкэнд, чтобы просто возвращать даты в виде чисел, как он предложил.

Однако, если бы я не сделал это еще одно более быстрое решение, я обнаружил, что это была функция сортировки, которая использует тот факт, что moment.js возвращает NaN, если он не может проанализировать строку:

let date1 = moment(a[orderBy]).valueOf();
let date2 = moment(b[orderBy]).valueOf();

    if(isNaN(date1) || isNaN(date2)){
      return 0;
    }

    if(date2 < date1){
      return -1;
    }
    if(date2 > date1){
      return 1;
    }
  };
...