给json数组中的元素排序

给json数组中的元素排序

起因

基本信息

  • 平台:windows

  • IDE:Lazarus 2.2.6

  • json包:FPC自带的fpjson

背景

最近在搞一个小工具,数据文件采用的是json格式,其中一个节点存放的是一组文件的基本信息的清单,这个节点自然就是个json数组,元素就是每个文件基本信息的json对象。界面展示用的是经典的DBGrid + DataSource + DataSet方案,所以会把json数组转为DataSet

操作过程中会比对磁盘上的文件,该添的添,该删的的删,该改的改,该标记的标记。一番操作下来,顺序自然是乱的,虽然可以操作DataSet或者换用带排序功能的DBGridEh达到排序的目的,但直接看json数据的话,依然是乱序的。因此,期望直接对json数组进行排序。

解决过程

习惯先看源码,如果没有原生解决方案了,或者原生解决方案太别扭了,才会选择第三方解决方案或自己造轮子。

一看源码

pascal 复制代码
TJSONArray = class(TJSONData)
public
  ...
  Procedure Sort(Compare: TListSortCompare);

TJSONArray已经提供了排序方法,不过这个参数是什么东西?

pascal 复制代码
TListSortCompare = function (Item1, Item2: Pointer): Integer;

嗯,是个函数声明,也就是说具体的算法实现要自己写,可以先找找看有默认的实现没。结果是:没有!那就自己写吧。

看声明,这个函数是要比较两个指针指向的东西,并返回一个整数。嗯,看上去很简单,不过:

  • 到底是怎么实现排序的?

  • 参数是指针,指向的又是什么东西?

  • 返回一个什么样的整数才能实现排序呢?

没懂!!!

还是接着看源码吧:

pascal 复制代码
procedure TJSONArray.Sort(Compare: TListSortCompare);
begin
  FList.Sort(Compare);
end; 

Procedure TFPObjectList.Sort(Compare: TListSortCompare);
begin
  FList.Sort(Compare);
end; 

procedure TFPList.Sort(Compare: TListSortCompare);
begin
  if Not Assigned(FList) or (FCount < 2) then exit;
  QuickSort(Flist, 0, FCount-1, Compare);
end;  

Procedure QuickSort(FList: PPointerList; L, R : Longint;
                     Compare: TListSortCompare);
var
  I, J : Longint;
  P, Q : Pointer;
begin
 repeat
   I := L;
   J := R;
   P := FList^[ (L + R) div 2 ];
   repeat
     while Compare(P, FList^[i]) > 0 do
       I := I + 1;
     while Compare(P, FList^[J]) < 0 do
       J := J - 1;
     If I <= J then
     begin
       Q := FList^[I];
       Flist^[I] := FList^[J];
       FList^[J] := Q;
       I := I + 1;
       J := J - 1;
     end;
   until I > J;
   // sort the smaller range recursively
   // sort the bigger range via the loop
   // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
   if J - L < R - I then
   begin
     if L < J then
       QuickSort(FList, L, J, Compare);
     L := I;
   end
   else
   begin
     if I < R then
       QuickSort(FList, I, R, Compare);
     R := J;
   end;
 until L >= R;
end;

嗯,原来调用了一个快排算法,算法中用到的比较两个元素的函数就是需要实现的函数。

再看源码

印象中,TStringList好像可以直接排序:

pascal 复制代码
procedure TStringList.Sort;
begin
  CustomSort(@StringListAnsiCompare);
end;  

procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
begin
  If (FCount>1) and (FForceSort or (FSortStyle<>sslAuto))  then
    begin
    Changing;
    QuickSort(0,FCount-1, CompareFn);
    Changed;
    end;
end;

嗯,同样是调用了一个快排算法,而且比较函数也是实现了的:

pascal 复制代码
function StringListAnsiCompare(List: TStringList; Index1, Index: Integer): Integer;
begin
  Result := List.DoCompareText(List.FList^[Index1].FString,
    List.FList^[Index].FString);
end;

function TStringList.DoCompareText(const s1, s2: string): PtrInt;
begin
  if FCaseSensitive then
  begin
    if UseLocale then
      result:=AnsiCompareStr(s1,s2)
    else
      result:=CompareStr(s1,s2);
  end else
  begin
    if UseLocale then
      result:=AnsiCompareText(s1,s2)
    else
      result:=CompareText(s1,s2);
  end;
end; 

原来如此!比较函数只要实现类似CompareTextCompareStr的效果就能被快排算法调用从而实现排序功能。

自己实现

由于json数组里的元素全部都是相同结构的json对象,那就简单粗暴点:

pascal 复制代码
function DefaltJsonCompare(Item1, Item2: Pointer): integer;
var
  s1, s2: string;
begin
  s1 := TJSONObject(Item1).Get('key', '');
  s2 := TJSONObject(Item2).Get('key', '');
  Result := CompareText(s1, s2);
end; 

测试一下,嗯,确实是期望的效果,完美收工!

总结

  1. 继承于TFPList的类,本质上都已实现了快排算法,也就是可以直接排序,所需要的仅仅是提供一个类似CompareText的比较函数的实现即可

  2. 基于TFPList组合并且以TFPList为主的类,也可以像TStringList一样封装一个方法实现排序,或者提供一个Helper实现排序

相关推荐
Kapaseker16 小时前
C# 斩获TIOBE年度编程语言
c#·编程语言
大熊猫侯佩17 小时前
Swift 6.2 列传(第十六篇):阿朱的“易容术”与阿紫的“毒药测试”
swift·编程语言·apple
superman超哥1 天前
Rust 引用的作用域与Non-Lexical Lifetimes(NLL):生命周期的精确革命
开发语言·后端·rust·生命周期·编程语言·rust引用的作用域·rust nll
脑洞AI食验员2 天前
为了开发 AI 智能体,我先用两天打牢 Python 基础
编程语言
superman超哥2 天前
Rust 所有权系统如何防止双重释放:编译期的内存安全保证
开发语言·后端·rust·编程语言·内存安全·rust所有权·双重释放
superman超哥2 天前
Rust Drop Trait 与资源清理机制:确定性析构的优雅实现
开发语言·后端·rust·编程语言·rust drop trait·资源清理机制·确定性析构
superman超哥2 天前
Rust 借用检查器的工作原理:编译期内存安全的守护者
开发语言·后端·rust·编程语言·rust借用检查器·编译期内存安全·借用检查器
superman超哥2 天前
Rust 复制语义(Copy Trait)与移动语义的区别:类型系统的精确控制
开发语言·后端·rust·编程语言·移动语义·rust复制语义·copy trait
superman超哥3 天前
Rust 堆内存与栈内存的所有权管理:精确控制的内存模型
开发语言·后端·rust·编程语言·内存模型·堆内存与栈内存·所有权管理
superman超哥3 天前
Rust 线程安全性保证(Send 与 Sync):编译期并发安全的类型系统
开发语言·后端·rust·编程语言·并发安全·send与sync·rust线程