diff src/layout/Layout.ts @ 108:5e6840229a05

rewrite freeStatements rendering to put more planning in layout
author drewp@bigasterisk.com
date Fri, 18 Mar 2022 11:57:38 -0700
parents 2468f2227d22
children 3cdbbd913f1d
line wrap: on
line diff
--- a/src/layout/Layout.ts	Sun Mar 13 22:02:30 2022 -0700
+++ b/src/layout/Layout.ts	Fri Mar 18 11:57:38 2022 -0700
@@ -3,25 +3,36 @@
 import Immutable from "immutable"; // mostly using this for the builtin equals() testing, since NamedNode(x)!=NamedNode(x)
 import { NamedNode, Quad, Store, Term } from "n3";
 import { rdf } from "./namespaces";
+import { uniqueSortedTerms } from "./rdf_value";
 import { TableDesc, ViewConfig } from "./ViewConfig";
 
 type UriSet = Immutable.Set<NamedNode>;
 export type TypeToSubjs = Immutable.Map<NamedNode, UriSet>;
 
-// https://github.com/rdfjs/N3.js/issues/265
-(NamedNode.prototype as any).hashCode = () => 0;
-
 interface ColumnHeader {
   rdfType: NamedNode;
   pred: NamedNode;
 }
+
 export interface AlignedTable {
   columnHeaders: ColumnHeader[];
   rows: (Term | null)[][]; // each row is 1 wider than columnHeaders since the 1st element is the subject for that row
 }
-interface FreeStatements {
-  statements: Quad[];
+
+export interface PredRow {
+  pred: NamedNode;
+  objs: Term[];
 }
+
+export interface SubjRow {
+  subj: NamedNode;
+  predRows: PredRow[];
+}
+
+export interface FreeStatements {
+  subjRows: SubjRow[];
+}
+
 export interface LayoutResult {
   sections: (AlignedTable | FreeStatements)[];
 }
@@ -100,6 +111,34 @@
   return Immutable.Set(subjectsToGather);
 }
 
+function freeStatmentsSection(stmts: Quad[]): FreeStatements {
+  const subjs: NamedNode[] = [];
+  stmts.forEach((q) => {
+    subjs.push(q.subject as NamedNode);
+  });
+  return {
+    subjRows: uniqueSortedTerms(subjs).map((subj) => {
+      const preds: NamedNode[] = [];
+      let po = Immutable.Map<NamedNode, Term[]>();
+      stmts.forEach((q) => {
+        if (q.subject.equals(subj)) {
+          const p = q.predicate as NamedNode;
+          preds.push(p);
+          po = po.set(p, po.get(p, []));
+          po.get(p)?.push(q.object as Term);
+        }
+      });
+
+      const rows: PredRow[] = [];
+      uniqueSortedTerms(preds).forEach((p) => {
+        rows.push({ pred: p, objs: uniqueSortedTerms(po.get(p, [])) });
+      });
+      return { subj: subj, predRows: rows };
+    }),
+  };
+}
+
+// The description of how this page should look: sections, tables, etc.
 export class Layout {
   constructor(public viewConfig?: ViewConfig) {}
   plan(graph: Store): LayoutResult {
@@ -130,7 +169,7 @@
     if (table) {
       res.sections.push(table.value());
     }
-    res.sections.push({ statements: ungrouped });
+    res.sections.push(freeStatmentsSection(ungrouped));
     return res;
   }
 }