skip to Main Content

I want to change priority of a draggedItem with a priority of a targetItem.

If they are the same, they should be updated correctly (draggedItem should get priority of targetItem and targetItem priority should be increased by one, because it is the same as priority of draggedItem).

The priority of all other items should also be updated correctly (based on new draggedItem and targetItem priority) and in the end result I should have an array of objects where all objects have different priority.

Thus, I have a function as follows:

const updatePriorities = (data, draggedItem, targetItem) => {
  const draggedItemPriority = draggedItem?.priority;
  const targetItemPriority = targetItem?.priority;

  if (draggedItemPriority === undefined || targetItemPriority === undefined || draggedItemPriority === null || targetItemPriority === null) {
    return data;
  }

  const minPriority = Math.min(draggedItemPriority, targetItemPriority);
  const maxPriority = Math.max(draggedItemPriority, targetItemPriority);

  const newData = map(data, (item) => {
    if (item.priority === draggedItemPriority) {
      return { ...item, priority: targetItemPriority };
    } else if (item.priority >= minPriority && item.priority <= maxPriority) {
      const priorityOffset = targetItemPriority < draggedItemPriority ? 1 : -1;
      return { ...item, priority: item.priority + priorityOffset };
    } 
    return item;
  });

  return orderBy(newData, 'priority');
};

Thus if all objects in the array have different priority, it works fine. Thus with data as follows:

const mockedData = [
  { name: 'Item 0', priority: 0 },
  { name: 'Item 1', priority: 1 },
  { name: 'Item 2', priority: 2 },
  { name: 'Item 3', priority: 3 },
  { name: 'Item 4', priority: 4 },
  { name: 'Item 5', priority: 5 }
];
const mockedDraggedItem1 = mockedData[2];
const mockedTargetItem1 = mockedData[1];
const result = updatePriorities(mockedData, mockedDraggedItem1, mockedTargetItem1)
// The result
[
  { name: 'Item 0', priority: 0 },
  { name: 'Item 2', priority: 1 },
  { name: 'Item 1', priority: 2 },
  { name: 'Item 3', priority: 3 },
  { name: 'Item 4', priority: 4 },
  { name: 'Item 5', priority: 5 }
];

I get correct result, the priority is updated correctly and Item 2 came to second place (got priority 1) and Item 1 went to third place (got priority 2). That is all fine.

But when I have two objects with the same priority in the array. For example:

const mockedData = [
  { name: 'Item 0', priority: 0 },
  { name: 'Item 1', priority: 1 },
  { name: 'Item 2', priority: 1 },
  { name: 'Item 3', priority: 3 },
  { name: 'Item 4', priority: 4 },
  { name: 'Item 5', priority: 5 }
];
const mockedDraggedItem1 = mockedData[2];
const mockedTargetItem1 = mockedData[1];

Then it does not work as expected, want the priority of draggedItem and targetItem is not updated.

I should get next result:

[
  { name: 'Item 0', priority: 0 },
  { name: 'Item 2', priority: 1 },
  { name: 'Item 1', priority: 2 },
  { name: 'Item 3', priority: 3 },
  { name: 'Item 4', priority: 4 },
  { name: 'Item 5', priority: 5 }
];

What should I do to update it correctly?

Thanks in advance.

2

Answers


  1. You can use loadash.sortBy function pass your array of objects to it and you can also pass your options with which you would like to sort it like with which properties you would like to sort an array

    _.sortBy(arrayOfobject, 
        [function(o) { return o.obj; }]);
    

    Example:

    const _ = require("lodash"); 
    var object = [
      { 'obj': 'moto', 'price': 19999 },
      { 'obj': 'oppo', 'price': 18999 },
      { 'obj': 'moto', 'price': 17999 },
      { 'obj': 'oppo', 'price': 15999 } ];
       
    // Use of _.sortBy() method
    let sorted = _.sortBy(object, ['obj']);
    
    Login or Signup to reply.
  2. If you want to sort even with equal priorities, then instead of considering priority, you need to examine the entire element. I have modified and commented your function accordingly. It is important to note that in this case, there should not be two completely identical elements in the array, although this could also be handled, but I did not address it now.

    Solution # 1

    • Actually, it’s not a perfect solution. A logical error occurs when there are elements of the same priority and they happen to be between the moved and target elements.
    /**
     ** For Lodash Compatibility
     */
    const { map, orderBy } = _
    
    /**
     ** Function
     */
    const updatePriorities = (data, draggedItem, targetItem) => {
      const draggedItemPriority = draggedItem?.priority;
      const targetItemPriority = targetItem?.priority;
    
      if (draggedItemPriority === undefined || targetItemPriority === undefined || draggedItemPriority === null || targetItemPriority === null) {
        return data;
      }
    
      // Calculate priority of dragged elements on new place
      // - example draggedItemPriority = 3, targetItemPriority = 1
      //   then 3 priority moved before 1 as new 1
      //   and old 1 changed to 2
      //   so maxPriority = 2
      const maxPriority = Math.max(draggedItemPriority, targetItemPriority) - Math.abs(draggedItemPriority - targetItemPriority);
    
      const newData = map(data, (item) => {
        // Current item is dragged
        if (item === draggedItem) {
          return { ...item, priority: targetItemPriority };
        }
        // Current item is target (need +1 or -1)
        else if (item === targetItem) {
          const priorityOffset = targetItemPriority < draggedItemPriority || targetItemPriority === draggedItemPriority ? 1 : -1;
          return { ...item, priority: item.priority + priorityOffset };
        }
        // Priority of Current item greater than maxPriority (need +1)
        else if (item.priority >= maxPriority) {
          return { ...item, priority: item.priority + 1 };
        }
        return item;
      });
      
      // Order By Priority
      return orderBy(newData, 'priority');
    };
    
    /**
     ** Data
     */
    const mockedData = [
      { name: 'Item 0', priority: 0 },
      { name: 'Item 1', priority: 1 },
      { name: 'Item 2', priority: 1 },
      { name: 'Item 3', priority: 2 },
      { name: 'Item 4', priority: 3 },
      { name: 'Item 5', priority: 4 }
    ];
    const mockedDraggedItem1 = mockedData[2];
    const mockedTargetItem1 = mockedData[1];
    
    /**
     ** Test
     */
    console.log(updatePriorities(mockedData, mockedDraggedItem1, mockedTargetItem1))
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

    Solution # 2

    • Instead, I came up with a solution where we adjust the priority of the element to be moved to a logically appropriate value, so that it is placed in front of the target element (if moving forward) or behind the target element (if moving backward) – to achieve this, I simply add or subtract 0.5. Then, using the orderBy() function, we sort the array and rearrange the priorities based on the indexes in the resulting array.
    • However, I disagree with sorting elements of similar priority because it assumes that we are displaying them based on their order in the array. Therefore, when dealing with similar elements, we can only sort them based on their index within the array.
    • With this solution, you always recalculate the priority of the same elements, which may not be suitable for you. You can modify the logic of the map following orderBy if you want to maintain the priority of non-moved and non-target elements.
    /**
     ** For Lodash Compatibility
     */
    const { map, orderBy } = _
    
    /**
     ** Function
     */
    const updatePriorities = (data, draggedItem, targetItem) => {
      const draggedItemPriority = draggedItem?.priority
      const targetItemPriority = targetItem?.priority
    
      if (draggedItemPriority === undefined || targetItemPriority === undefined || draggedItemPriority === null || targetItemPriority === null) {
        return data
      }
      
      // Get index of draggedItem and targetItem (to can check when have same priorities)
      const draggedItemIndex = data.findIndex((item) => item === draggedItem)
      const targetItemIndex = data.findIndex((item) => item === targetItem)
      
      // Set new priority for draggedItem
      // - if draggedItem move to forward, then new priority greater than targetPriority
      //   - OR have same priorities and targetItemIndex greater than draggedItemIndex
      // - else draggedItem move to backward, so new prioirty smaller than targetPriority
      data[draggedItemIndex].priority = targetItemPriority > draggedItemPriority || (targetItemPriority === draggedItemPriority && targetItemIndex > draggedItemIndex)
        ? targetItemPriority + 0.5
        : targetItemPriority - 0.5
      
      // Order By Priority, and set new priority values by index
      return map(orderBy(data, 'priority'), (item, index) => {
        item.priority = index
        return item
      })
    }
    
    /**
     ** Data
     */
    const mockedData = [
      { name: 'Item 0', priority: 0 },
      { name: 'Item 1', priority: 1 },
      { name: 'Item 2', priority: 1 },
      { name: 'Item 3', priority: 2 },
      { name: 'Item 4', priority: 3 },
      { name: 'Item 5', priority: 4 }
    ]
    const mockedDraggedItem1 = mockedData[2]
    const mockedTargetItem1 = mockedData[1]
    
    /**
     ** Test
     */
    console.log(updatePriorities(mockedData, mockedDraggedItem1, mockedTargetItem1))
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search