简体   繁体   中英

Efficient Human-Readable Date String Sorting Algorithm in Javascript

I'm trying to sort thousands of date strings from my database in my React table. The component that I'm using allows users to sort information by clicking on the topmost row, the API is here .

I'd initially programmed my sort function to check whether the strings being fed into the sort were valid, using moment.js , and if they are, to get the total millisecond value of that date string, subtract it from the subsequent value in the array, and then return the result.

However, turns out that this process is labor intensive, and is causing the sort function to lag when it runs on the browser. The relevant bit of code looks like this:

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;
  }

Because of the way my table displays dates (as human-readable strings) I need some way to sort the strings by their times more efficiently. Does anyone have any suggestions on how to accomplish this? I'm sorting thousands of values and this solution is again, too slow.

Thanks!

I assume these strings are part of your state data. Rather than just storing strings (if that's what you're doing), I'd store objects with the date value as a number (milliseconds-since-The-Epoch) and the human-readable string seprately. Then the sort callback could use the number directly, rather than having to constantly re-parse the strings.

Live Example:

 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> 

TJ's answer works. I ended up changing my back-end to just return the dates as numbers, as he suggested.

However, if I hadn't done this another faster solution I found was this sort function, which takes advantage of the fact that moment.js returns NaN if it's unable to parse a string:

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;
    }
  };

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM