给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实现排序

相关推荐
用户05956611920911 小时前
深入理解Spring Boot框架:从基础到实践
java·spring·编程语言
whysqwhw1 天前
PlantUML 全面指南
编程语言
whysqwhw1 天前
ASCII图表及工具
编程语言
Mirageef1 天前
aardio 继承与多态
编程语言
大熊猫侯佩2 天前
Swift 中强大的 Key Paths(键路径)机制趣谈(下)
swift·编程语言·apple
Mirageef3 天前
aardio 类与对象基础
编程语言
Mirageef5 天前
aardio 自动识别验证码输入
编程语言
冒泡的肥皂6 天前
强大的ANTLR4语法解析器入门demo
后端·搜索引擎·编程语言
程序员岳焱6 天前
Java 程序员成长记(三):菜鸟入职之@Transactional「罢工」
java·后端·编程语言