写了一个 [排序只需要修改一个对象数据] 的模式

348 天前
 rizon

废话:
最近在对 notelive.cc 这个网站做一个新版本(不会替代这个网站,只是做一个新项目),然后遇到了目录树的排序老问题。

正题:
出于降低数据库写压力,简化初始化数据结构,以及我就是不喜欢排序要修改两个对象的个人原因等,因此做了这个排序模型。 实现效果:

  1. 数据结构初始化不需要包含排序信息(个人业务需要,默认按照时间排序,本 demo 里按照 id 排序)
  2. 排序时只需要修改拖动的对象的数据,不需要操作目标位置的对象。

有什么好的优化建议,也欢迎贴出来。

"use client";
import { useRef, useState } from "react";

interface Item {
  id: number;
  orderTime: number | undefined;
  pre: number | null | undefined;
}
let init: Item[] = [
  {
    id: 1,
    orderTime: undefined,
    pre: undefined,
  },
  {
    id: 4,
    orderTime: undefined,
    pre: undefined,
  },
  {
    id: 2,
    orderTime: undefined,
    pre: 3,
  },
  {
    id: 3,
    orderTime: undefined,
    pre: undefined,
  },
];
export default function SortPage() {
  const source = useRef<HTMLInputElement>(null);
  const target = useRef<HTMLInputElement>(null);
  const [list, setList] = useState<Item[]>(init);

  const doSort = () => {
    if (!source.current || !source.current.value) {
      setList(reSort(list));
      return;
    }

    const sourceItem = list.find(
      (item) => item.id == parseInt(source.current!.value)
    );
    if (!sourceItem) {
      alert("not fount sourceItem");
      return;
    }
    const targetItem = target.current!.value
      ? list.find((item) => item.id == parseInt(target.current!.value))
      : null;

    // 只需要修改 sourceItem 的值,不用处理 targetItem
    if (!targetItem) {
      sourceItem.orderTime = Date.now();
      sourceItem.pre = -1;
    } else {
      sourceItem.orderTime = Date.now();
      sourceItem.pre = targetItem.id;
    }
    setList(reSort(list));
  };
  return (
    <div>
      <div>
        <div>
          移动的元素: <input className="border" ref={source} type="text" />
        </div>
        <div>
          目标元素的后面(留空首位):{" "}
          <input className="border" ref={target} type="text" />
        </div>
        <div>
          <button className="bg-gray-400 p-2" onClick={doSort}>
            移动
          </button>
        </div>
      </div>
      <div className="mt-5 flex items-center justify-start gap-x-2 px-3">
        {list.map((item) => {
          return (
            <div className="bg-blue-100 p-2 " key={item.id}>
              <p>id: {item.id}</p>
              <p>orderTime: {item.orderTime}</p>
              <p>pre: {item.pre}</p>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function reSort(originList: Item[]) {
  const list = [...originList];
  const sortById = (pre: Item, next: Item) => {
    // id 从小到大排序
    if (pre.id > next.id) {
      return 1;
    } else if (pre.id < next.id) {
      return -1;
    }
    return 0;
  };
  const sortByOrderTime = (pre: Item, next: Item) => {
    let preOrderTime = pre.orderTime || 0;
    let nextOrderTime = next.orderTime || 0;
    // orderTime 从小到大排序
    if (preOrderTime > nextOrderTime) {
      return 1;
    } else if (preOrderTime < nextOrderTime) {
      return -1;
    }
    return 0;
  };

  list.sort(sortById).sort(sortByOrderTime);

  const checkList = [...list];

  // 按照 orderTime 从旧到新,每个元素都执行一次 pre 位置调整处理,如果两个元素的 pre 相同,orderTime 最晚的则会插入到旧数据的前面
  for (let i = 0; i < checkList.length; i++) {
    const checkItem = checkList[i];
    if (checkItem.pre) {
      moveElementAfter(list, checkItem.id, checkItem.pre);
    }
  }

  function moveElementAfter(
    array: Item[],
    sourceId: number,
    targetItemId: number
  ) {
    const sourceIdx = array.findIndex((e) => e.id === sourceId);
    if (sourceIdx === -1) {
      return;
    }
    const sourceItem = array[sourceIdx];

    const indexToInsert = list.findIndex((e) => e.id === targetItemId);
    array.splice(sourceIdx, 1);
    if (indexToInsert > -1) {
      array.splice(indexToInsert + 1, 0, sourceItem); // 在目标元素后面插入
    } else {
      array.unshift(sourceItem); // 如果找不到元素 b ,则将元素 a 放在数组首位
    }
  }

  return list;
}


文末: 如果大家对 notelive.cc 这个网站有什么建议,或对正在内测的新笔记产品有兴趣,或单纯想要唠唠嗑,可以加微信群哈。

1270 次点击
所在节点    程序员
7 条回复
rizon
348 天前
擦 有问题,有些场景不对。忽略吧
renmu
348 天前
只要取到目标前后对象的 sort 值,然后将这这两个值和的一半写入目标就可以了
zhy0216
348 天前
@renmu 最后会有精度问题的
renmu
348 天前
@zhy0216 你先算一下多少次能达到精度问题,这是一个很小概率的事情,就算有,完全可以做个额外处理
zhy0216
348 天前
@renmu 只要有存在的可能就要额外处理 感觉不够好而已
rizon
347 天前
@renmu #2
@zhy0216 #3 以前也想过这个办法,两个元素之间留数字的空间,这样每次排序只改变一个的值。
但是时间久了还要执行一次刷库操作,不然总有用完的时候,或者就想说的精度问题
rizon
347 天前
@rizon #6 而且这个方案,要求第一次写入的数据就要有顺序。
我其实核心想实现的事,如果未做排序,就不产生大量的排序数据,按创建时间降序排序即可。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/999157

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX