dataview排序

sort 函数介绍

/**
 * Return a sorted array sorted by the given key; an optional comparator can be provided, which will
 * be used to compare the keys in leiu of the default dataview comparator.
 */
sort<U>(key: ArrayFunc<T, U>, direction?: "asc" | "desc", comparator?: ArrayComparator<U>): DataArray<T>;

这个函数签名表示一个排序方法,它接受三个参数:

  1. key (ArrayFunc<T, U>): 这是一个从数组元素类型 T 映射到比较键类型 U 的函数。例如,如果你有一个对象数组,这个函数可以用来提取每个对象中的某个属性(比如 ctimetitle)作为排序的依据。

  2. direction ("asc" | "desc",可选): 指定排序的方向,可以是 "asc"(升序)或 "desc"(降序)。默认可能是根据实现决定的,但通常没有明确指定时默认为升序。

  3. comparator (ArrayComparator<U>,可选): 这是一个可选的比较器函数,类型为 ArrayComparator<U>,用于自定义如何比较由 key 函数提取出的键值 U。这个参数允许你提供自己的逻辑来决定两个键值之间的大小关系,而不是使用默认的比较逻辑(通常是基于 <> 操作符的比较)。

如何使用第三个参数(comparator)

假设你有以下数据结构和排序需求:

interface Item {
    id: number;
    name: string;
    customValue: number;
}

const items: Item[] = [
    { id: 1, name: "Item 2", customValue: 10 },
    { id: 2, name: "Item 1", customValue: 5 },
    // ...
];

// 自定义比较器,例如,我们想让customValue相等时,根据id降序
const customComparator = (a: number, b: number): number => {
    if (a === b) {
        // 当customValue相等时,根据id降序排列
        return b - a;
    } else {
        return a - b; // 否则,常规升序排列
    }
};

// 调用排序方法,按customValue排序,方向为升序,但使用自定义比较器
const sortedItems = items.sort((item) => item.customValue, "asc", customComparator);

在这个例子中,customComparator 作为第三个参数传递给了 sort 方法,它会在比较 customValue 字段时被用到,允许你控制在特定条件下的排序逻辑,比如当 customValue 相等时,进一步根据 id 进行排序。

怎么先按 customValue desc,再按 name asc

要使用上述函数实现先按 customValue 降序排列,再在 customValue 相同的情况下按 name 升序排列,你需要定义一个复合的排序逻辑。在这种情况下,你可以直接在 key 函数中实现这个逻辑,或者创建一个复合键(combination key)来同时考虑两个排序条件。然而,由于提供的函数签名不直接支持复合键或多重排序条件作为单独参数,我们可以采取一种变通方式,即在 comparator 参数中实现这个复合逻辑。这里是如何做的:

interface Item {
    id: number;
    name: string;
    customValue: number;
}

const items: Item[] = [
    { id: 1, name: "Item 2", customValue: 10 },
    { id: 2, name: "Item 1", customValue: 5 },
    // 其他项...
];

// 自定义比较器,实现复合排序逻辑
const compositeComparator = (a: Item, b: Item): number => {
    // 首先比较customValue,降序
    if (a.customValue !== b.customValue) {
        return b.customValue - a.customValue; // 降序
    }
    // 如果customValue相同,则按name升序
    return a.name.localeCompare(b.name); // 升序
};

// 调用排序方法,仅需自定义比较器即可实现复合排序
const sortedItems = items.sort(compositeComparator);

console.log(sortedItems);

在这个例子中,我们没有直接使用 sort 函数签名中提供的第三个参数 comparator(因为我们没有直接比较单一的键值),而是直接定义了一个比较函数 compositeComparator,它接收两个 Item 对象并比较它们的 customValue(降序)以及在 customValue 相同时比较它们的 name(升序)。然后,我们直接使用这个比较器对 items 进行排序,而不需要显式指定 keydirection,因为我们的比较逻辑已经覆盖了这两个方面。

不支持 interface 怎么办

如果不支持 TypeScript 的 interface,你可能是在使用原生 JavaScript 或者一个不支持接口定义的环境。在这种情况下,你可以直接使用对象字面量来定义你的数据结构,并通过传统的 JavaScript 函数来实现排序逻辑。下面是如何在不使用 interface 的情况下实现同样的排序需求:

// 数据模型示例
const items = [
    { id: 1, name: "Item 2", customValue: 10 },
    { id: 2, name: "Item 1", customValue: 5 },
    // 其他项...
];

// 复合比较器函数,用于实现复合排序逻辑
function compositeComparator(a, b) {
    // 首先比较customValue,降序
    if (a.customValue !== b.customValue) {
        return b.customValue - a.customValue; // 降序
    }
    // 如果customValue相同,则按name升序
    return a.name.localeCompare(b.name); // 升序
}

// 调用数组的sort方法,并传入比较器函数
const sortedItems = items.sort(compositeComparator);

console.log(sortedItems);

这段代码展示了如何在不使用 TypeScript 接口的情况下,直接使用 JavaScript 对象和函数来定义数据结构和排序逻辑。通过定义一个比较函数,我们依然能够实现在多个条件下的复合排序。