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

相关推荐
咕噜Yuki06092 天前
Java基础篇:学会这些技能,程序员职场竞争力UP UP
java·开发语言·编程语言
codeGoogle2 天前
计算机书籍打包
前端·后端·编程语言
Moonbit3 天前
MoonBit 双周报 Vol.59:新增编译器常量支持,改进未使用警告,支持跨包函数导入...多个关键技术持续优化中!
编程语言
神经星星3 天前
【TVM 教程】外部张量函数
人工智能·开源·编程语言
Moonbit7 天前
MGPIC案例分享|零基础早鸟教程:8小时使用 wasm4 开发井子棋小游戏!
编程语言
邓校长的编程课堂9 天前
少儿编程进入义务教育课程:培养信息科技素养的新政策解读
科技·编程语言·少儿编程·信息学竞赛·科技特长生·义务教育
Moonbit17 天前
MoonBit 双周报 Vol.58:原生后端支持、多行字符串插值、json.inspect 功能等多项关键特性取得显著进展!
编程语言
小尤笔记1 个月前
Python操作系统的6个自动化脚本
数据库·python·自动化·php·编程语言·python入门
神经星星1 个月前
【TVM 教程】使用 Relay Visualizer 可视化 Relay
人工智能·机器学习·编程语言
GoppViper2 个月前
golang学习笔记17——golang使用go-kit框架搭建微服务详解
笔记·后端·学习·微服务·golang·编程语言·go-kit