0

I have a function that sorts courses (divs) based on an end date that is declared in a data attribute. The entire list of classes is sorting correct except one. I cannot figure out why it is not sorting it correctly. The March 24th is the first one in the sorted list.

I am guessing it is only looking at the "2" in "24" but I am not sure how to get around this, if it is the case.

HTML

 <button onClick="courseSort()">SORT</button>

    <div id="course-container" class="row pb-5 pt-5">
        <div class="class-item col-12 col-md-6 pb-4" data-class-start="1/1/2022" data-class-end="5/28/2022">
            <h5>May 28</h5>
        </div>
        <div class="class-item col-12 col-md-6 pb-4" data-class-start="2/8/2022" data-class-end="3/9/2022">
            <h5>Mar 9</h5>
        </div>
        <div class="class-item col-12 col-md-6 pb-4" data-class-start="1/1/2022" data-class-end="3/5/2022">
            <h5>Mar 5</h5>
        </div>
        <div class="class-item col-12 col-md-6 pb-4" data-class-start="1/1/2022" data-class-end="4/1/2022">
            <h5>Apr 1</h5>
        </div>
        <div class="class-item col-12 col-md-6 pb-4" data-class-start="2/19/2022" data-class-end="3/8/2022">
            <h5>Mar 8</h5>
        </div>
        <div class="class-item col-12 col-md-6 pb-4" data-class-start="2/19/2022" data-class-end="3/24/2022">
            <h5>Mar 24</h5>
        </div>
        <div class="class-item col-12 col-md-6 pb-4" data-class-start="1/1/2022" data-class-end="4/23/2022">
            <h5>Apr 23</h5> 
        </div>
        <div class="class-item col-12 col-md-6 pb-4" data-class-start="1/1/2022" data-class-end="4/30/2022">
            <h5>Apr 30</h5>
        </div>
    </div>

JS

function sorter(a, b) {
    if (a.dataset.classEnd < b.dataset.classEnd)
        return -1;
    if (a.dataset.classEnd > b.dataset.classEnd)
        return 1;
    return 0;
}
  
// Function to sort Data
function courseSort() {
    var course = document.querySelectorAll("[data-class-end]");
    var courseArray = Array.from(course);
    let sorted = courseArray.sort(sorter);
    sorted.forEach(e =>
        document.querySelector("#course-container").appendChild(e));
}

Thanks

nahoneyc
  • 30
  • 2
  • 3
    The `sorter` function correctly performs a lexicographic sort and your `forEach` correctly reappends elements in the sorted order. Dates are usually provided in the [ISO 8601 format](/q/9576860/4642212) to allow for easy sorting. I suggest you convert the dates to this format. – Sebastian Simon Feb 26 '22 at 21:50

2 Answers2

0

Check function sorter(a, b). When you're accessing for example a.dataset.classEnd, it's a string, not a date. You may want to do something like:

function sorter(a, b) {
  const [aDate, bDate] = [new Date(a.dataset.classEnd), new Date(b.dataset.classEnd)];
  if (aDate < bDate) return -1;
  if (aDate > bDate) return 1;
  return 0;
}

Also, you could simply do this like a one-liner instead of a separated function:

let sorted = courseArray.sort((a, b) => new Date(a.dataset.classEnd) - new Date(b.dataset.classEnd));

I am guessing it is only looking at the "2" in "24" but I am not sure how to get around this, if it is the case.

Yes, that's exactly what was happening as you were comparing strings.

EDIT: As Sebastian Simon suggested in comments, you may like to change the format of your dates to allow sorting without really needing to convert the variables to date.

Luka Cerrutti
  • 667
  • 1
  • 4
  • 9
  • _“Note: Parsing of date strings with the `Date` constructor (and `Date.parse`, which works the same way) is **strongly discouraged** due to browser differences and inconsistencies.”_ — From the [documentation](//developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date/Date#parameters). – Sebastian Simon Feb 26 '22 at 21:51
  • See [Parsing a string to a date in JavaScript](/q/5619202/4642212). Something as “simple” as `const mdyDate = (dateString) => { const { year, month, date } = dateString.match(/(?\d+)\/(?\d+)\/(?\d+)/)?.groups ?? {}; return new Date(year, month - 1, date); };` and then `.sort(({ dataset: { classEnd: a } }, { dataset: { classEnd: b } }) => mdyDate(a) - mdyDate(b))` is probably enough. – Sebastian Simon Feb 26 '22 at 22:03
0

As already mentioned in comments, the true value of a date can be derived from the Date object. In the example below, the function courseArray(nodeList) will:

  • convert the NodeList into an array run it through .map()
    [...nodeList].map(d => {...
    // [...nodeList] is the same as Array.from(nodeList)
    
  • extract the value from data-class-end and convert it to ISO format
    let mmddyyyy = d.dataset.classEnd;
    let yyyymmdd = mmddyyyy.split('/');
    let yyyy = yyyymmdd.pop();
    yyyymmdd.unshift(yyyy);
    let ISO = yyyymmdd.join('-');
    // from mm/dd/yyyy to yyyy-mm-dd
    
  • then it is converted to the number of milli-seconds since 1-1-1970 to the ISO date -- then it's added as data-order to the div and then the divs are reordered according to data-order value.
      let ms = Date.parse(ISO);
      d.dataset.order = ms;
      return d;
    }).sort((a, b) => a.dataset.order - b.dataset.order);
    

Also, the inline event handler was removed and a onevent property event handler was added. See why inline event handlers are garbage.

const courseArray = nodeList => {
  return [...nodeList].map(d => {
    let mmddyyyy = d.dataset.classEnd;
    let yyyymmdd = mmddyyyy.split('/');
    let yyyy = yyyymmdd.pop();
    yyyymmdd.unshift(yyyy);
    let ISO = yyyymmdd.join('-');
    let ms = Date.parse(ISO);
    d.dataset.order = ms;
    return d;
  }).sort((a, b) => a.dataset.order - b.dataset.order);
};

function courseSort() {
  const course = document.querySelectorAll(".class-item");

  let sorted = courseArray(course);
  sorted.forEach(e =>
    document.querySelector("#course-container").appendChild(e));
};

document.querySelector('button').onclick = courseSort;
<button>SORT</button>

<div id="course-container" class="row pb-5 pt-5">
  <div class="class-item col-12 col-md-6 pb-4" data-class-start="1/1/2022" data-class-end="5/28/2022">
    <h5>May 28</h5>
  </div>
  <div class="class-item col-12 col-md-6 pb-4" data-class-start="2/8/2022" data-class-end="3/9/2022">
    <h5>Mar 9</h5>
  </div>
  <div class="class-item col-12 col-md-6 pb-4" data-class-start="1/1/2022" data-class-end="3/5/2022">
    <h5>Mar 5</h5>
  </div>
  <div class="class-item col-12 col-md-6 pb-4" data-class-start="1/1/2022" data-class-end="4/1/2022">
    <h5>Apr 1</h5>
  </div>
  <div class="class-item col-12 col-md-6 pb-4" data-class-start="2/19/2022" data-class-end="3/8/2022">
    <h5>Mar 8</h5>
  </div>
  <div class="class-item col-12 col-md-6 pb-4" data-class-start="2/19/2022" data-class-end="3/24/2022">
    <h5>Mar 24</h5>
  </div>
  <div class="class-item col-12 col-md-6 pb-4" data-class-start="1/1/2022" data-class-end="4/23/2022">
    <h5>Apr 23</h5>
  </div>
  <div class="class-item col-12 col-md-6 pb-4" data-class-start="1/1/2022" data-class-end="4/30/2022">
    <h5>Apr 30</h5>
  </div>
</div>
zer00ne
  • 41,936
  • 6
  • 41
  • 68