Home > Enterprise >  How can I insert new list items sorted by attribute value?
How can I insert new list items sorted by attribute value?

Time:07-15

Let's imagine we have an HTML message list. Each item has index - Unix timestamp with milliseconds.

<ul>
 <li ts="1657744925386">Item 1</li>
 <li ts="1657744941061">Item 2</li>
 <li ts="1657744953364">Item 3</li>
 <li ts="1657744964372">Item 4</li>
 <li ts="1657744973219">Item 5</li>
 <li ts="1657744978450">Item 6</li>
</ul>

Then we have to insert another item, with ts=1657744950123. It should be after Item 3.

Is there an efficient way to calculate the new position in the list so that jQuery can insert the new element (because it requires a reference element relative to which will be inserted after or before the new element), given that there can be a lot of messages on the page and we don't have possibility to walk through all of them every time, making comparisons?

CodePudding user response:

Assuming no existing data structure representing the list, I'd loop over the list elements, checking the attribute value, until I found the first larger value. That's the index 1 at which you'll insert.

If there is a data array, you could do something similar on the array itself and regenerate the list.

Here's a wild 'n woolly example of the former approach. If you need the text nodes to also be updated, you probably want to take approach #2 and rebuild the list programmatically.

const list = document.querySelector('ul');
const items = list.querySelectorAll('li');
const newItemVal = 1657744950123;
let itemAfterIndex;

for (const [index, item] of items.entries()) {
  const tsVal = parseInt(item.getAttribute('ts'));

  if (tsVal > newItemVal) {
    itemAfterIndex = index;
    break;
  }
}

const newItem = document.createElement('li');
newItem.setAttribute('ts', newItemVal);
newItem.textContent = 'New item';

const itemAfter = list.querySelectorAll('li')[itemAfterIndex];
list.insertBefore(newItem, itemAfter);
<ul>
  <li ts="1657744925386">Item 1</li>
  <li ts="1657744941061">Item 2</li>
  <li ts="1657744953364">Item 3</li>
  <li ts="1657744964372">Item 4</li>
  <li ts="1657744973219">Item 5</li>
  <li ts="1657744978450">Item 6</li>
</ul>

CodePudding user response:

Consider the following jQuery example.

$(function() {
  var myNewItem = $("<li>", {
    ts: 1657744950123
  }).html("Item 7");
  $("ul li").each(function(i, el) {
    console.log($(el).attr("ts"), myNewItem.attr("ts"));
    if ($(el).attr("ts") > myNewItem.attr("ts")) {
      myNewItem.insertBefore(el);
      return false;
    }
  });
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
  <li ts="1657744925386">Item 1</li>
  <li ts="1657744941061">Item 2</li>
  <li ts="1657744953364">Item 3</li>
  <li ts="1657744964372">Item 4</li>
  <li ts="1657744973219">Item 5</li>
  <li ts="1657744978450">Item 6</li>
</ul>

This assumes the list is already sorted. If not, you can append the new item anyplace, detach all the items, sort them, and append them back.

$(function() {
  $("<li>", {
    ts: 1657744950123
  }).html("Item 7").appendTo("ul");
  var myItems = $("ul li").detach();
  myItems.sort(function(a, b) {
    if ($(a).attr("ts") < $(b).attr("ts")) {
      return -1;
    }
    if ($(a).attr("ts") > $(b).attr("ts")) {
      return 1;
    }
    if ($(a).attr("ts") == $(b).attr("ts")) {
      return 0;
    }
  });
  $("ul").append(myItems);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
  <li ts="1657744925386">Item 1</li>
  <li ts="1657744941061">Item 2</li>
  <li ts="1657744953364">Item 3</li>
  <li ts="1657744964372">Item 4</li>
  <li ts="1657744973219">Item 5</li>
  <li ts="1657744978450">Item 6</li>
</ul>

See more: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

  • Related