@graphty/graph-io 0.3.40 → 0.3.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/chunks/{children-CL3Cy0ez.js → children-CA4coBi0.js} +36 -27
- package/dist/chunks/children-CA4coBi0.js.map +1 -0
- package/dist/chunks/{choose-CS3fvGT1.js → choose-DvpbdRuz.js} +2 -2
- package/dist/chunks/{choose-CS3fvGT1.js.map → choose-DvpbdRuz.js.map} +1 -1
- package/dist/chunks/{constants-DqKZT6cH.js → constants-fT3E8JYY.js} +2 -2
- package/dist/chunks/{constants-DqKZT6cH.js.map → constants-fT3E8JYY.js.map} +1 -1
- package/dist/chunks/{escape-BJRLd3vJ.js → escape-B62ZGM_a.js} +2 -2
- package/dist/chunks/{escape-BJRLd3vJ.js.map → escape-B62ZGM_a.js.map} +1 -1
- package/dist/chunks/{export-B-aVCwyG.js → export-rKLeGQMo.js} +404 -243
- package/dist/chunks/export-rKLeGQMo.js.map +1 -0
- package/dist/chunks/{exporter-6ocL_9x_.js → exporter-8hLNemFk.js} +4 -4
- package/dist/chunks/{exporter-6ocL_9x_.js.map → exporter-8hLNemFk.js.map} +1 -1
- package/dist/chunks/{exporter-BVXnV9-9.js → exporter-BWwEuOPT.js} +29 -17
- package/dist/chunks/exporter-BWwEuOPT.js.map +1 -0
- package/dist/chunks/{exporter-Bcp-l4Bo.js → exporter-CpRyHZ3H.js} +574 -475
- package/dist/chunks/exporter-CpRyHZ3H.js.map +1 -0
- package/dist/chunks/{exporter-SFYkDzD0.js → exporter-yLxKBvLE.js} +23 -20
- package/dist/chunks/{exporter-SFYkDzD0.js.map → exporter-yLxKBvLE.js.map} +1 -1
- package/dist/chunks/{importer-DcKB97kW.js → importer-BbNb7eNg.js} +111 -71
- package/dist/chunks/importer-BbNb7eNg.js.map +1 -0
- package/dist/chunks/{importer-D_srHyj6.js → importer-C98dpSH4.js} +5 -5
- package/dist/chunks/{importer-D_srHyj6.js.map → importer-C98dpSH4.js.map} +1 -1
- package/dist/chunks/{importer-BJlGAOht.js → importer-D794XUTe.js} +3 -3
- package/dist/chunks/{importer-BJlGAOht.js.map → importer-D794XUTe.js.map} +1 -1
- package/dist/chunks/{importer-AjwOJrn2.js → importer-Dz7tI8Sa.js} +202 -181
- package/dist/chunks/importer-Dz7tI8Sa.js.map +1 -0
- package/dist/chunks/{importer-DEp_fYjf.js → importer-Ht7LNg8L.js} +92 -64
- package/dist/chunks/importer-Ht7LNg8L.js.map +1 -0
- package/dist/chunks/{importer-CPqnIY8m.js → importer-WrGRZb0N.js} +42 -29
- package/dist/chunks/importer-WrGRZb0N.js.map +1 -0
- package/dist/chunks/{importer-BpuS3J8C.js → importer-xZ-3suY1.js} +74 -61
- package/dist/chunks/importer-xZ-3suY1.js.map +1 -0
- package/dist/chunks/{json-elements-DpO6AXIc.js → json-elements-DKSqpNW1.js} +3 -3
- package/dist/chunks/{json-elements-DpO6AXIc.js.map → json-elements-DKSqpNW1.js.map} +1 -1
- package/dist/chunks/{ontology-export-50L9DOV4.js → ontology-export-CtRXjg_i.js} +2 -2
- package/dist/chunks/{ontology-export-50L9DOV4.js.map → ontology-export-CtRXjg_i.js.map} +1 -1
- package/dist/chunks/{records-r3SFHz11.js → records-Cu4bxYOm.js} +69 -36
- package/dist/chunks/records-Cu4bxYOm.js.map +1 -0
- package/dist/chunks/{text-Bjfa1xYL.js → text-DHOJXnED.js} +20 -11
- package/dist/chunks/text-DHOJXnED.js.map +1 -0
- package/dist/csv.js +323 -241
- package/dist/csv.js.map +1 -1
- package/dist/cx.js +1 -1
- package/dist/cx2.js +2 -2
- package/dist/cys.js +1 -1
- package/dist/dot.js +1 -1
- package/dist/gexf.js +195 -97
- package/dist/gexf.js.map +1 -1
- package/dist/gml.js +170 -149
- package/dist/gml.js.map +1 -1
- package/dist/graph-io.js +27 -27
- package/dist/graphml.js +2 -2
- package/dist/json.js +1 -1
- package/dist/neo4j.js +103 -65
- package/dist/neo4j.js.map +1 -1
- package/dist/obo.js +1 -1
- package/dist/pajek.js +1 -1
- package/dist/src/children.d.ts.map +1 -1
- package/dist/src/children.js +50 -27
- package/dist/src/children.js.map +1 -1
- package/dist/src/common/attributes.d.ts.map +1 -1
- package/dist/src/common/attributes.js +86 -42
- package/dist/src/common/attributes.js.map +1 -1
- package/dist/src/common/input.d.ts.map +1 -1
- package/dist/src/common/input.js +358 -184
- package/dist/src/common/input.js.map +1 -1
- package/dist/src/common/lists.js +30 -15
- package/dist/src/common/lists.js.map +1 -1
- package/dist/src/common/text.d.ts +7 -0
- package/dist/src/common/text.d.ts.map +1 -1
- package/dist/src/common/text.js +15 -7
- package/dist/src/common/text.js.map +1 -1
- package/dist/src/common/zip.d.ts.map +1 -1
- package/dist/src/common/zip.js +36 -19
- package/dist/src/common/zip.js.map +1 -1
- package/dist/src/formats/csv/exporter.d.ts.map +1 -1
- package/dist/src/formats/csv/exporter.js +288 -146
- package/dist/src/formats/csv/exporter.js.map +1 -1
- package/dist/src/formats/csv/importer.d.ts.map +1 -1
- package/dist/src/formats/csv/importer.js +129 -80
- package/dist/src/formats/csv/importer.js.map +1 -1
- package/dist/src/formats/csv/records.d.ts.map +1 -1
- package/dist/src/formats/csv/records.js +78 -36
- package/dist/src/formats/csv/records.js.map +1 -1
- package/dist/src/formats/cys/tables.d.ts.map +1 -1
- package/dist/src/formats/cys/tables.js +11 -8
- package/dist/src/formats/cys/tables.js.map +1 -1
- package/dist/src/formats/dot/tokenizer.d.ts +39 -0
- package/dist/src/formats/dot/tokenizer.d.ts.map +1 -1
- package/dist/src/formats/dot/tokenizer.js +112 -66
- package/dist/src/formats/dot/tokenizer.js.map +1 -1
- package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
- package/dist/src/formats/gexf/exporter.js +65 -37
- package/dist/src/formats/gexf/exporter.js.map +1 -1
- package/dist/src/formats/gexf/importer.d.ts.map +1 -1
- package/dist/src/formats/gexf/importer.js +138 -50
- package/dist/src/formats/gexf/importer.js.map +1 -1
- package/dist/src/formats/gml/exporter.d.ts.map +1 -1
- package/dist/src/formats/gml/exporter.js +51 -35
- package/dist/src/formats/gml/exporter.js.map +1 -1
- package/dist/src/formats/gml/importer.d.ts.map +1 -1
- package/dist/src/formats/gml/importer.js +103 -58
- package/dist/src/formats/gml/importer.js.map +1 -1
- package/dist/src/formats/gml/syntax.d.ts.map +1 -1
- package/dist/src/formats/gml/syntax.js +17 -8
- package/dist/src/formats/gml/syntax.js.map +1 -1
- package/dist/src/formats/graphml/exporter.d.ts.map +1 -1
- package/dist/src/formats/graphml/exporter.js +34 -24
- package/dist/src/formats/graphml/exporter.js.map +1 -1
- package/dist/src/formats/graphml/importer.d.ts.map +1 -1
- package/dist/src/formats/graphml/importer.js +17 -13
- package/dist/src/formats/graphml/importer.js.map +1 -1
- package/dist/src/formats/graphml/tree.js +23 -14
- package/dist/src/formats/graphml/tree.js.map +1 -1
- package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
- package/dist/src/formats/graphml/yfiles.js +22 -10
- package/dist/src/formats/graphml/yfiles.js.map +1 -1
- package/dist/src/formats/json/dialect.d.ts.map +1 -1
- package/dist/src/formats/json/dialect.js +54 -20
- package/dist/src/formats/json/dialect.js.map +1 -1
- package/dist/src/formats/json/exporter.d.ts.map +1 -1
- package/dist/src/formats/json/exporter.js +38 -21
- package/dist/src/formats/json/exporter.js.map +1 -1
- package/dist/src/formats/json/importer.d.ts.map +1 -1
- package/dist/src/formats/json/importer.js +536 -343
- package/dist/src/formats/json/importer.js.map +1 -1
- package/dist/src/formats/json/obographs.js +29 -17
- package/dist/src/formats/json/obographs.js.map +1 -1
- package/dist/src/formats/neo4j/exporter.d.ts.map +1 -1
- package/dist/src/formats/neo4j/exporter.js +73 -43
- package/dist/src/formats/neo4j/exporter.js.map +1 -1
- package/dist/src/formats/neo4j/header.d.ts.map +1 -1
- package/dist/src/formats/neo4j/header.js +37 -16
- package/dist/src/formats/neo4j/header.js.map +1 -1
- package/dist/src/formats/neo4j/importer.d.ts.map +1 -1
- package/dist/src/formats/neo4j/importer.js +21 -11
- package/dist/src/formats/neo4j/importer.js.map +1 -1
- package/dist/src/formats/obo/exporter.d.ts.map +1 -1
- package/dist/src/formats/obo/exporter.js +18 -7
- package/dist/src/formats/obo/exporter.js.map +1 -1
- package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
- package/dist/src/formats/pajek/exporter.js +142 -73
- package/dist/src/formats/pajek/exporter.js.map +1 -1
- package/dist/src/formats/pajek/importer.d.ts.map +1 -1
- package/dist/src/formats/pajek/importer.js +26 -15
- package/dist/src/formats/pajek/importer.js.map +1 -1
- package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
- package/dist/src/formats/pajek/syntax.js +85 -58
- package/dist/src/formats/pajek/syntax.js.map +1 -1
- package/dist/src/formats/xgmml/emit.d.ts +7 -0
- package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
- package/dist/src/formats/xgmml/emit.js +79 -40
- package/dist/src/formats/xgmml/emit.js.map +1 -1
- package/dist/src/formats/xgmml/exporter.d.ts.map +1 -1
- package/dist/src/formats/xgmml/exporter.js +38 -12
- package/dist/src/formats/xgmml/exporter.js.map +1 -1
- package/dist/src/formats/xgmml/importer.d.ts.map +1 -1
- package/dist/src/formats/xgmml/importer.js +38 -18
- package/dist/src/formats/xgmml/importer.js.map +1 -1
- package/dist/src/sniff.js +109 -57
- package/dist/src/sniff.js.map +1 -1
- package/dist/xgmml.js +2 -2
- package/package.json +2 -2
- package/src/children.ts +54 -27
- package/src/common/attributes.ts +118 -49
- package/src/common/input.ts +439 -197
- package/src/common/lists.ts +29 -15
- package/src/common/text.ts +24 -15
- package/src/common/zip.ts +47 -18
- package/src/formats/csv/exporter.ts +377 -175
- package/src/formats/csv/importer.ts +155 -84
- package/src/formats/csv/records.ts +90 -35
- package/src/formats/cys/tables.ts +11 -10
- package/src/formats/dot/tokenizer.ts +118 -68
- package/src/formats/gexf/exporter.ts +67 -36
- package/src/formats/gexf/importer.ts +173 -63
- package/src/formats/gml/exporter.ts +78 -82
- package/src/formats/gml/importer.ts +119 -68
- package/src/formats/gml/syntax.ts +18 -8
- package/src/formats/graphml/exporter.ts +44 -24
- package/src/formats/graphml/importer.ts +18 -11
- package/src/formats/graphml/tree.ts +22 -12
- package/src/formats/graphml/yfiles.ts +23 -10
- package/src/formats/json/dialect.ts +61 -22
- package/src/formats/json/exporter.ts +40 -21
- package/src/formats/json/importer.ts +701 -385
- package/src/formats/json/obographs.ts +35 -15
- package/src/formats/neo4j/exporter.ts +79 -41
- package/src/formats/neo4j/header.ts +50 -16
- package/src/formats/neo4j/importer.ts +21 -10
- package/src/formats/obo/exporter.ts +34 -17
- package/src/formats/pajek/exporter.ts +180 -117
- package/src/formats/pajek/importer.ts +27 -15
- package/src/formats/pajek/syntax.ts +99 -58
- package/src/formats/xgmml/emit.ts +88 -40
- package/src/formats/xgmml/exporter.ts +40 -12
- package/src/formats/xgmml/importer.ts +45 -23
- package/src/sniff.ts +115 -59
- package/dist/chunks/children-CL3Cy0ez.js.map +0 -1
- package/dist/chunks/export-B-aVCwyG.js.map +0 -1
- package/dist/chunks/exporter-BVXnV9-9.js.map +0 -1
- package/dist/chunks/exporter-Bcp-l4Bo.js.map +0 -1
- package/dist/chunks/importer-AjwOJrn2.js.map +0 -1
- package/dist/chunks/importer-BpuS3J8C.js.map +0 -1
- package/dist/chunks/importer-CPqnIY8m.js.map +0 -1
- package/dist/chunks/importer-DEp_fYjf.js.map +0 -1
- package/dist/chunks/importer-DcKB97kW.js.map +0 -1
- package/dist/chunks/records-r3SFHz11.js.map +0 -1
- package/dist/chunks/text-Bjfa1xYL.js.map +0 -1
package/dist/csv.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { b as plural, bU as DICT_SAMPLE_ROWS, ac as DictHeuristic, R as ROLE_TAKEN_CODE$1, I as ID_MERGED_CODE$1, m as DUPLICATE_EDGE_ID_CODE$1, n as DUPLICATE_NODE_CODE$1, t as MISSING_ID_CODE$1, s as MISSING_ENDPOINT_CODE$1, E as EMPTY_INPUT_CODE$1, al as resolveImportOptions, a8 as ImportReportBuilder, ao as reportSinkOptions, an as reportUnusedOptions, aq as DirectionResolver, ar as IdCoercer, am as throwIfAborted, O as OPTION_IGNORED_CODE, aK as parseWeightText, a0 as uniqueColumnName, ay as RENAMED_CODE, a1 as declareResolved, c as capabilities, L as LOSS, a as checkCapabilities, r as resolveExportOptions, p as pairFolding,
|
|
2
|
-
import { i as inferTextDtype, T as TextCellWriter, p as parseTextCell, W as WIDENING_UNSUPPORTED_CODE } from "./chunks/text-
|
|
1
|
+
import { b as plural, bU as DICT_SAMPLE_ROWS, ac as DictHeuristic, R as ROLE_TAKEN_CODE$1, I as ID_MERGED_CODE$1, m as DUPLICATE_EDGE_ID_CODE$1, n as DUPLICATE_NODE_CODE$1, t as MISSING_ID_CODE$1, s as MISSING_ENDPOINT_CODE$1, E as EMPTY_INPUT_CODE$1, al as resolveImportOptions, a8 as ImportReportBuilder, ao as reportSinkOptions, an as reportUnusedOptions, aq as DirectionResolver, ar as IdCoercer, am as throwIfAborted, O as OPTION_IGNORED_CODE, aK as parseWeightText, a0 as uniqueColumnName, ay as RENAMED_CODE, a1 as declareResolved, c as capabilities, L as LOSS, a as checkCapabilities, r as resolveExportOptions, p as pairFolding, $ as agree, e as explicitWeights, b_ as otherFormatDialect, bD as countMixedEdges, bS as joinListText, aG as formatInteger, aF as formatDecimal, aH as canonicalId, M as MIXED_DIRECTION_CODE, D as DIRECTION_FORCED_CODE, d as DIRECTION_REFUSED_CODE, S as SINK_OPTION_CODE, C as COLUMN_RENAMED_CODE, w as UNKNOWN_ENCODING_CODE, x as ENCODING_FALLBACK_CODE, y as INVALID_ENCODING_CODE, z as INVALID_UTF8_CODE, P as PRECISION_CODE, F as ELEMENT_ISSUE, G as INPUT_ISSUE } from "./chunks/export-rKLeGQMo.js";
|
|
2
|
+
import { i as inferTextDtype, T as TextCellWriter, p as parseTextCell, W as WIDENING_UNSUPPORTED_CODE } from "./chunks/text-DHOJXnED.js";
|
|
3
3
|
import { GraphFormatError, INVALID_INDEX } from "@graphty/graph-format";
|
|
4
|
-
import { s as stripLeadingComments, a as sniffNewline, b as sniffDelimiter, D as DELIMITER_CANDIDATES,
|
|
5
|
-
import { o as quoteCsvCell } from "./chunks/escape-
|
|
4
|
+
import { s as stripLeadingComments, a as sniffNewline, b as sniffDelimiter, D as DELIMITER_CANDIDATES, C as CsvRecordReader, d as splitRecords, c as checkRecordSyntax, B as BAD_QUOTE_CODE, U as UNCLOSED_QUOTE_CODE } from "./chunks/records-Cu4bxYOm.js";
|
|
5
|
+
import { o as quoteCsvCell } from "./chunks/escape-B62ZGM_a.js";
|
|
6
6
|
import { j as joinText, e as encodeChunks } from "./chunks/writer-DxjgHwSe.js";
|
|
7
7
|
const SOURCE_NAMES = Object.freeze([
|
|
8
8
|
"source",
|
|
@@ -336,6 +336,38 @@ function isPadded(text, quoted) {
|
|
|
336
336
|
}
|
|
337
337
|
function resolveCsvOptions(options) {
|
|
338
338
|
const o = options ?? {};
|
|
339
|
+
checkDialectOptions(o);
|
|
340
|
+
for (const name of ["sourceColumn", "targetColumn", "idColumn", "labelColumn"]) {
|
|
341
|
+
checkColumnRef(name, o[name]);
|
|
342
|
+
}
|
|
343
|
+
if (o.typeColumn !== null) {
|
|
344
|
+
checkColumnRef("typeColumn", o.typeColumn);
|
|
345
|
+
}
|
|
346
|
+
if (o.table === "adjacency") {
|
|
347
|
+
checkAdjacencyOptions(o);
|
|
348
|
+
}
|
|
349
|
+
if (o.rowNumberIds !== void 0 && typeof o.rowNumberIds !== "boolean") {
|
|
350
|
+
throw new GraphFormatError("E_UNSUPPORTED", "option rowNumberIds: expected a boolean", {
|
|
351
|
+
option: "rowNumberIds",
|
|
352
|
+
found: o.rowNumberIds
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
return {
|
|
356
|
+
delimiter: o.delimiter ?? null,
|
|
357
|
+
decimalComma: o.decimal === ",",
|
|
358
|
+
header: o.header ?? "auto",
|
|
359
|
+
table: o.table ?? "auto",
|
|
360
|
+
sourceColumn: o.sourceColumn ?? null,
|
|
361
|
+
targetColumn: o.targetColumn ?? null,
|
|
362
|
+
typeColumn: o.typeColumn,
|
|
363
|
+
idColumn: o.idColumn ?? null,
|
|
364
|
+
labelColumn: o.labelColumn ?? null,
|
|
365
|
+
nodes: nodesInput(o.nodes),
|
|
366
|
+
rowNumberIds: o.rowNumberIds ?? false,
|
|
367
|
+
explicitDirected: typeof options?.defaultDirected === "boolean" ? options.defaultDirected : null
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
function checkDialectOptions(o) {
|
|
339
371
|
if (o.delimiter !== void 0 && (typeof o.delimiter !== "string" || o.delimiter.length === 0)) {
|
|
340
372
|
throw new GraphFormatError("E_UNSUPPORTED", "option delimiter: expected a non-empty string", {
|
|
341
373
|
option: "delimiter",
|
|
@@ -369,48 +401,22 @@ function resolveCsvOptions(options) {
|
|
|
369
401
|
found: o.table
|
|
370
402
|
});
|
|
371
403
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
for (const name of ["sourceColumn", "targetColumn", "typeColumn", "idColumn", "labelColumn"]) {
|
|
380
|
-
if (o[name] !== void 0) {
|
|
381
|
-
throw new GraphFormatError("E_UNSUPPORTED", `option ${name}: an adjacency table has no columns`, {
|
|
382
|
-
option: name,
|
|
383
|
-
found: o[name]
|
|
384
|
-
});
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
if (o.rowNumberIds === true) {
|
|
388
|
-
throw new GraphFormatError("E_UNSUPPORTED", "option rowNumberIds: an adjacency table names its nodes", {
|
|
389
|
-
option: "rowNumberIds",
|
|
390
|
-
found: o.rowNumberIds
|
|
404
|
+
}
|
|
405
|
+
function checkAdjacencyOptions(o) {
|
|
406
|
+
for (const name of ["sourceColumn", "targetColumn", "typeColumn", "idColumn", "labelColumn"]) {
|
|
407
|
+
if (o[name] !== void 0) {
|
|
408
|
+
throw new GraphFormatError("E_UNSUPPORTED", `option ${name}: an adjacency table has no columns`, {
|
|
409
|
+
option: name,
|
|
410
|
+
found: o[name]
|
|
391
411
|
});
|
|
392
412
|
}
|
|
393
413
|
}
|
|
394
|
-
if (o.rowNumberIds
|
|
395
|
-
throw new GraphFormatError("E_UNSUPPORTED", "option rowNumberIds:
|
|
414
|
+
if (o.rowNumberIds === true) {
|
|
415
|
+
throw new GraphFormatError("E_UNSUPPORTED", "option rowNumberIds: an adjacency table names its nodes", {
|
|
396
416
|
option: "rowNumberIds",
|
|
397
417
|
found: o.rowNumberIds
|
|
398
418
|
});
|
|
399
419
|
}
|
|
400
|
-
return {
|
|
401
|
-
delimiter: o.delimiter ?? null,
|
|
402
|
-
decimalComma: o.decimal === ",",
|
|
403
|
-
header: o.header ?? "auto",
|
|
404
|
-
table: o.table ?? "auto",
|
|
405
|
-
sourceColumn: o.sourceColumn ?? null,
|
|
406
|
-
targetColumn: o.targetColumn ?? null,
|
|
407
|
-
typeColumn: o.typeColumn,
|
|
408
|
-
idColumn: o.idColumn ?? null,
|
|
409
|
-
labelColumn: o.labelColumn ?? null,
|
|
410
|
-
nodes: nodesInput(o.nodes),
|
|
411
|
-
rowNumberIds: o.rowNumberIds ?? false,
|
|
412
|
-
explicitDirected: typeof options?.defaultDirected === "boolean" ? options.defaultDirected : null
|
|
413
|
-
};
|
|
414
420
|
}
|
|
415
421
|
function checkColumnRef(name, value) {
|
|
416
422
|
if (value === void 0) {
|
|
@@ -433,6 +439,15 @@ function isBlank(text) {
|
|
|
433
439
|
function isUnset(text, quoted) {
|
|
434
440
|
return quoted !== true && isBlank(text);
|
|
435
441
|
}
|
|
442
|
+
function setNeighborCells(row, quoted) {
|
|
443
|
+
let neighbors = 0;
|
|
444
|
+
for (let k = 1; k < row.length; k++) {
|
|
445
|
+
if (!isUnset(row[k], quoted[k])) {
|
|
446
|
+
neighbors++;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return neighbors;
|
|
450
|
+
}
|
|
436
451
|
const ABORT_CHECK_INTERVAL = 64;
|
|
437
452
|
function uniqueNames(names, report, line) {
|
|
438
453
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -564,39 +579,11 @@ class TableReader {
|
|
|
564
579
|
const firstLine = this.reader.line;
|
|
565
580
|
const firstQuoted = this.reader.quoted.slice(0, firstRow.length);
|
|
566
581
|
const pending = [];
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
mode = true;
|
|
573
|
-
}
|
|
574
|
-
if (mode === "auto") {
|
|
575
|
-
const second = await iterator.next();
|
|
576
|
-
const secondRow = second.done ? null : second.value;
|
|
577
|
-
const secondLine = this.reader.line;
|
|
578
|
-
const secondQuoted = this.reader.quoted.slice(0, secondRow?.length ?? 0);
|
|
579
|
-
header = looksLikeHeader(firstRow, secondRow) || this.namesExplicitColumn(firstRow);
|
|
580
|
-
if (!header) {
|
|
581
|
-
pending.push({ row: firstRow, quoted: firstQuoted, line: firstLine });
|
|
582
|
-
}
|
|
583
|
-
if (secondRow !== null) {
|
|
584
|
-
pending.push({ row: secondRow, quoted: secondQuoted, line: secondLine });
|
|
585
|
-
}
|
|
586
|
-
} else {
|
|
587
|
-
header = mode;
|
|
588
|
-
if (!header) {
|
|
589
|
-
pending.push({ row: firstRow, quoted: firstQuoted, line: firstLine });
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
if (this.rowNumbers && !header) {
|
|
593
|
-
report.warning(
|
|
594
|
-
"unsupported",
|
|
595
|
-
OPTION_IGNORED_CODE,
|
|
596
|
-
"option rowNumberIds ignored: a headerless node table takes its ids from column 0",
|
|
597
|
-
{ line: firstLine, element: "rowNumberIds" }
|
|
598
|
-
);
|
|
599
|
-
}
|
|
582
|
+
const header = await this.decideHeader(
|
|
583
|
+
iterator,
|
|
584
|
+
{ row: firstRow, quoted: firstQuoted, line: firstLine },
|
|
585
|
+
pending
|
|
586
|
+
);
|
|
600
587
|
const names = header ? uniqueNames(headerNames(firstRow), report, firstLine) : positionalNames(firstRow.length);
|
|
601
588
|
const last = firstRow.length - 1;
|
|
602
589
|
const optionalLast = header && last > 0 && isUnset(firstRow[last], firstQuoted[last]);
|
|
@@ -626,6 +613,70 @@ class TableReader {
|
|
|
626
613
|
for (const { row, quoted, line } of pending) {
|
|
627
614
|
this.processRow(row, quoted, line);
|
|
628
615
|
}
|
|
616
|
+
await this.readRemainingRows(iterator);
|
|
617
|
+
if (this.dataRows === 0 && header) {
|
|
618
|
+
report.warning("missing-value", NO_DATA_ROWS_CODE, "the table has a header and no data rows", {
|
|
619
|
+
line: firstLine
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
this.reportReaderFindings();
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Decide whether the first row is the header, reading the second row when the header option is
|
|
626
|
+
* "auto", and queue the rows read so far that are data.
|
|
627
|
+
* @param iterator - the record iterator, past the first row
|
|
628
|
+
* @param first - the first row
|
|
629
|
+
* @param pending - receives the data rows read so far, in order
|
|
630
|
+
* @returns whether the first row is the header
|
|
631
|
+
*/
|
|
632
|
+
async decideHeader(iterator, first, pending) {
|
|
633
|
+
const mode = this.headerMode();
|
|
634
|
+
let second = null;
|
|
635
|
+
let header;
|
|
636
|
+
if (mode === "auto") {
|
|
637
|
+
const next = await iterator.next();
|
|
638
|
+
second = next.done ? null : { row: next.value, quoted: this.reader.quoted.slice(0, next.value.length), line: this.reader.line };
|
|
639
|
+
header = looksLikeHeader(first.row, second?.row ?? null) || this.namesExplicitColumn(first.row);
|
|
640
|
+
} else {
|
|
641
|
+
header = mode;
|
|
642
|
+
}
|
|
643
|
+
if (!header) {
|
|
644
|
+
pending.push(first);
|
|
645
|
+
}
|
|
646
|
+
if (second !== null) {
|
|
647
|
+
pending.push(second);
|
|
648
|
+
}
|
|
649
|
+
if (this.rowNumbers && !header) {
|
|
650
|
+
this.state.report.warning(
|
|
651
|
+
"unsupported",
|
|
652
|
+
OPTION_IGNORED_CODE,
|
|
653
|
+
"option rowNumberIds ignored: a headerless node table takes its ids from column 0",
|
|
654
|
+
{ line: first.line, element: "rowNumberIds" }
|
|
655
|
+
);
|
|
656
|
+
}
|
|
657
|
+
return header;
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* The header option as this table reads it: an adjacency table has no header unless the caller
|
|
661
|
+
* says so (its rows vary in width); a table numbered by rowNumberIds has one, since a headerless
|
|
662
|
+
* table takes its ids from column 0.
|
|
663
|
+
* @returns true, false or "auto"
|
|
664
|
+
*/
|
|
665
|
+
headerMode() {
|
|
666
|
+
const mode = this.state.csv.header;
|
|
667
|
+
if (mode === "auto" && this.kind === "adjacency") {
|
|
668
|
+
return false;
|
|
669
|
+
}
|
|
670
|
+
if (mode === "auto" && this.rowNumbers) {
|
|
671
|
+
return true;
|
|
672
|
+
}
|
|
673
|
+
return mode;
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* Push every row after the header and the rows read to decide it, then finish the column writers.
|
|
677
|
+
* @param iterator - the record iterator
|
|
678
|
+
*/
|
|
679
|
+
async readRemainingRows(iterator) {
|
|
629
680
|
const { signal } = this.state.common;
|
|
630
681
|
let sinceCheck = 0;
|
|
631
682
|
for (; ; ) {
|
|
@@ -642,12 +693,6 @@ class TableReader {
|
|
|
642
693
|
for (const writer of this.writers) {
|
|
643
694
|
writer?.finish();
|
|
644
695
|
}
|
|
645
|
-
if (this.dataRows === 0 && header) {
|
|
646
|
-
report.warning("missing-value", NO_DATA_ROWS_CODE, "the table has a header and no data rows", {
|
|
647
|
-
line: firstLine
|
|
648
|
-
});
|
|
649
|
-
}
|
|
650
|
-
this.reportReaderFindings();
|
|
651
696
|
}
|
|
652
697
|
/**
|
|
653
698
|
* Whether a row is Excel's delimiter directive (`sep=;`) under a delimiter the caller gave (a
|
|
@@ -1199,12 +1244,7 @@ class TableReader {
|
|
|
1199
1244
|
const { where } = this;
|
|
1200
1245
|
where.line = line;
|
|
1201
1246
|
where.element = null;
|
|
1202
|
-
|
|
1203
|
-
for (let k = 1; k < row.length; k++) {
|
|
1204
|
-
if (!isUnset(row[k], quoted[k])) {
|
|
1205
|
-
neighbors++;
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1247
|
+
const neighbors = setNeighborCells(row, quoted);
|
|
1208
1248
|
if (isUnset(row[0], quoted[0])) {
|
|
1209
1249
|
report.error("missing-value", MISSING_ID_CODE, `blank node cell`, { line });
|
|
1210
1250
|
counts.skippedNodes++;
|
|
@@ -1926,19 +1966,23 @@ function nodeTableNote(names, table, notes) {
|
|
|
1926
1966
|
);
|
|
1927
1967
|
}
|
|
1928
1968
|
}
|
|
1969
|
+
function idTextCollisions(ids) {
|
|
1970
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1971
|
+
let collisions = 0;
|
|
1972
|
+
for (let i = 0; i < ids.size; i++) {
|
|
1973
|
+
const text = String(ids.idOf(i));
|
|
1974
|
+
if (seen.has(text)) {
|
|
1975
|
+
collisions++;
|
|
1976
|
+
} else {
|
|
1977
|
+
seen.add(text);
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
return collisions;
|
|
1981
|
+
}
|
|
1929
1982
|
function idNotes(snapshot, values, note) {
|
|
1930
1983
|
const { ids } = snapshot;
|
|
1931
1984
|
if (ids.kind === "mixed") {
|
|
1932
|
-
const
|
|
1933
|
-
let collisions = 0;
|
|
1934
|
-
for (let i = 0; i < ids.size; i++) {
|
|
1935
|
-
const text = String(ids.idOf(i));
|
|
1936
|
-
if (seen.has(text)) {
|
|
1937
|
-
collisions++;
|
|
1938
|
-
} else {
|
|
1939
|
-
seen.add(text);
|
|
1940
|
-
}
|
|
1941
|
-
}
|
|
1985
|
+
const collisions = idTextCollisions(ids);
|
|
1942
1986
|
if (collisions > 0) {
|
|
1943
1987
|
note(
|
|
1944
1988
|
CSV_LOSS.ID_TEXT_COLLISION,
|
|
@@ -1983,6 +2027,44 @@ function planExport(snapshot, options, values) {
|
|
|
1983
2027
|
edgeRows.push(e);
|
|
1984
2028
|
}
|
|
1985
2029
|
}
|
|
2030
|
+
directionNotes(snapshot, csv, edgeRows.length, note);
|
|
2031
|
+
if (folding.mutualCount > 0 && csv.table !== "nodes") {
|
|
2032
|
+
note(
|
|
2033
|
+
CSV_LOSS.MUTUAL_EXPANDED,
|
|
2034
|
+
`${folding.mutualCount} mutual pair${plural(folding.mutualCount)} ${agree(folding.mutualCount, "is", "are")} written as two directed rows; the mutual mark is lost`,
|
|
2035
|
+
null,
|
|
2036
|
+
folding.mutualCount
|
|
2037
|
+
);
|
|
2038
|
+
}
|
|
2039
|
+
if (csv.table === "edges") {
|
|
2040
|
+
noteNodeCoverage(snapshot, edgeRows, note);
|
|
2041
|
+
}
|
|
2042
|
+
const weights = explicitWeights(snapshot);
|
|
2043
|
+
const { edgeId, edgeLabel, edgeColumns } = planEdgeColumns(snapshot, csv, edgeRows, values, note);
|
|
2044
|
+
const { nodeLabel, nodeColumns } = planNodeColumns(snapshot, csv, values, note);
|
|
2045
|
+
if (csv.table !== "nodes") {
|
|
2046
|
+
nodeTableNote(
|
|
2047
|
+
[...nodeLabel === null ? [] : [nodeLabel.meta.name], ...nodeColumns.map((c) => c.column.meta.name)],
|
|
2048
|
+
csv.table,
|
|
2049
|
+
notes
|
|
2050
|
+
);
|
|
2051
|
+
}
|
|
2052
|
+
return {
|
|
2053
|
+
csv,
|
|
2054
|
+
common,
|
|
2055
|
+
notes,
|
|
2056
|
+
edgeRows,
|
|
2057
|
+
folding,
|
|
2058
|
+
edgeId,
|
|
2059
|
+
edgeLabel,
|
|
2060
|
+
weights,
|
|
2061
|
+
edgeColumns,
|
|
2062
|
+
nodeLabel,
|
|
2063
|
+
nodeColumns,
|
|
2064
|
+
idText
|
|
2065
|
+
};
|
|
2066
|
+
}
|
|
2067
|
+
function directionNotes(snapshot, csv, edgeRowCount, note) {
|
|
1986
2068
|
if (csv.table !== "nodes" && (csv.dialect.type === null || csv.table === "adjacency")) {
|
|
1987
2069
|
const where = csv.table === "adjacency" ? "an adjacency table" : "the generic dialect";
|
|
1988
2070
|
const mixed = countMixedEdges(snapshot);
|
|
@@ -2001,7 +2083,7 @@ function planExport(snapshot, options, values) {
|
|
|
2001
2083
|
mixed
|
|
2002
2084
|
);
|
|
2003
2085
|
}
|
|
2004
|
-
} else if (csv.table === "edges" && !snapshot.directed &&
|
|
2086
|
+
} else if (csv.table === "edges" && !snapshot.directed && edgeRowCount === 0) {
|
|
2005
2087
|
note(
|
|
2006
2088
|
CSV_LOSS.DIRECTION_DROPPED,
|
|
2007
2089
|
"the direction lives in the Type cell of each edge row; an undirected graph without edges reads back as directed",
|
|
@@ -2009,18 +2091,8 @@ function planExport(snapshot, options, values) {
|
|
|
2009
2091
|
0
|
|
2010
2092
|
);
|
|
2011
2093
|
}
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
CSV_LOSS.MUTUAL_EXPANDED,
|
|
2015
|
-
`${folding.mutualCount} mutual pair${plural(folding.mutualCount)} ${agree(folding.mutualCount, "is", "are")} written as two directed rows; the mutual mark is lost`,
|
|
2016
|
-
null,
|
|
2017
|
-
folding.mutualCount
|
|
2018
|
-
);
|
|
2019
|
-
}
|
|
2020
|
-
if (csv.table === "edges") {
|
|
2021
|
-
noteNodeCoverage(snapshot, edgeRows, note);
|
|
2022
|
-
}
|
|
2023
|
-
const weights = explicitWeights(snapshot);
|
|
2094
|
+
}
|
|
2095
|
+
function planEdgeColumns(snapshot, csv, edgeRows, values, note) {
|
|
2024
2096
|
const edgeId = snapshot.edges.byRole("id");
|
|
2025
2097
|
const edgeLabel = snapshot.edges.byRole("label");
|
|
2026
2098
|
const edgeReserved = new Set([csv.dialect.source, csv.dialect.target, csv.dialect.weight].map(lower));
|
|
@@ -2053,6 +2125,9 @@ function planExport(snapshot, options, values) {
|
|
|
2053
2125
|
checkValues(column, "edge", edgeRows, note);
|
|
2054
2126
|
}
|
|
2055
2127
|
}
|
|
2128
|
+
return { edgeId, edgeLabel, edgeColumns };
|
|
2129
|
+
}
|
|
2130
|
+
function planNodeColumns(snapshot, csv, values, note) {
|
|
2056
2131
|
const nodeLabel = snapshot.nodes.byRole("label");
|
|
2057
2132
|
const nodeReserved = /* @__PURE__ */ new Set([lower(csv.dialect.id)]);
|
|
2058
2133
|
const nodeRows = [];
|
|
@@ -2069,34 +2144,13 @@ function planExport(snapshot, options, values) {
|
|
|
2069
2144
|
nodeLabel,
|
|
2070
2145
|
csv.table === "nodes" ? note : null
|
|
2071
2146
|
);
|
|
2072
|
-
if (csv.table === "nodes") {
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
checkValues(column, "node", nodeRows, note);
|
|
2077
|
-
}
|
|
2147
|
+
if (csv.table === "nodes" && values) {
|
|
2148
|
+
checkRoleColumn(nodeLabel, "label", LABEL_NAMES, "node", nodeRows, note);
|
|
2149
|
+
for (const { column } of nodeColumns) {
|
|
2150
|
+
checkValues(column, "node", nodeRows, note);
|
|
2078
2151
|
}
|
|
2079
|
-
} else {
|
|
2080
|
-
nodeTableNote(
|
|
2081
|
-
[...nodeLabel === null ? [] : [nodeLabel.meta.name], ...nodeColumns.map((c) => c.column.meta.name)],
|
|
2082
|
-
csv.table,
|
|
2083
|
-
notes
|
|
2084
|
-
);
|
|
2085
2152
|
}
|
|
2086
|
-
return {
|
|
2087
|
-
csv,
|
|
2088
|
-
common,
|
|
2089
|
-
notes,
|
|
2090
|
-
edgeRows,
|
|
2091
|
-
folding,
|
|
2092
|
-
edgeId,
|
|
2093
|
-
edgeLabel,
|
|
2094
|
-
weights,
|
|
2095
|
-
edgeColumns,
|
|
2096
|
-
nodeLabel,
|
|
2097
|
-
nodeColumns,
|
|
2098
|
-
idText
|
|
2099
|
-
};
|
|
2153
|
+
return { nodeLabel, nodeColumns };
|
|
2100
2154
|
}
|
|
2101
2155
|
function noteNodeCoverage(snapshot, edgeRows, note) {
|
|
2102
2156
|
const { nodeCount } = snapshot;
|
|
@@ -2152,39 +2206,44 @@ function attributeColumns(table, domain, reserved, idColumn, labelColumn, note)
|
|
|
2152
2206
|
if (role !== null && SKIPPED_ROLES.has(role)) {
|
|
2153
2207
|
continue;
|
|
2154
2208
|
}
|
|
2155
|
-
const low = lower(name);
|
|
2156
2209
|
const set = column.length - column.nullCount;
|
|
2157
|
-
|
|
2158
|
-
if (reserved.has(low) || isIdName || idColumn !== null && lower(idColumn.meta.name) === low) {
|
|
2159
|
-
note?.(
|
|
2160
|
-
CSV_LOSS.RESERVED_NAME,
|
|
2161
|
-
`${domain} column "${name}" is not written: the name is reserved for the ${isIdName ? "edge id" : low} column`,
|
|
2162
|
-
name,
|
|
2163
|
-
set
|
|
2164
|
-
);
|
|
2210
|
+
if (isReservedName(name, domain, reserved, idColumn, set, note)) {
|
|
2165
2211
|
continue;
|
|
2166
2212
|
}
|
|
2167
|
-
if (findColumn([name], LABEL_NAMES) >= 0) {
|
|
2168
|
-
|
|
2169
|
-
note?.(
|
|
2170
|
-
CSV_LOSS.RESERVED_NAME,
|
|
2171
|
-
`${domain} column "${name}" is not written: the name is reserved for the label column`,
|
|
2172
|
-
name,
|
|
2173
|
-
set
|
|
2174
|
-
);
|
|
2175
|
-
continue;
|
|
2176
|
-
}
|
|
2177
|
-
note?.(
|
|
2178
|
-
CSV_LOSS.ROLE_ASSUMED,
|
|
2179
|
-
`${domain} column "${name}" reads back with the label role (string)`,
|
|
2180
|
-
name,
|
|
2181
|
-
set
|
|
2182
|
-
);
|
|
2213
|
+
if (findColumn([name], LABEL_NAMES) >= 0 && !labelNameWritten(name, domain, labelColumn, set, note)) {
|
|
2214
|
+
continue;
|
|
2183
2215
|
}
|
|
2184
2216
|
out.push({ column, header: name });
|
|
2185
2217
|
}
|
|
2186
2218
|
return out;
|
|
2187
2219
|
}
|
|
2220
|
+
function isReservedName(name, domain, reserved, idColumn, set, note) {
|
|
2221
|
+
const low = lower(name);
|
|
2222
|
+
const isIdName = domain === "edge" ? findColumn([name], EDGE_ID_NAMES) >= 0 : false;
|
|
2223
|
+
if (reserved.has(low) || isIdName || idColumn !== null && lower(idColumn.meta.name) === low) {
|
|
2224
|
+
note?.(
|
|
2225
|
+
CSV_LOSS.RESERVED_NAME,
|
|
2226
|
+
`${domain} column "${name}" is not written: the name is reserved for the ${isIdName ? "edge id" : low} column`,
|
|
2227
|
+
name,
|
|
2228
|
+
set
|
|
2229
|
+
);
|
|
2230
|
+
return true;
|
|
2231
|
+
}
|
|
2232
|
+
return false;
|
|
2233
|
+
}
|
|
2234
|
+
function labelNameWritten(name, domain, labelColumn, set, note) {
|
|
2235
|
+
if (labelColumn !== null) {
|
|
2236
|
+
note?.(
|
|
2237
|
+
CSV_LOSS.RESERVED_NAME,
|
|
2238
|
+
`${domain} column "${name}" is not written: the name is reserved for the label column`,
|
|
2239
|
+
name,
|
|
2240
|
+
set
|
|
2241
|
+
);
|
|
2242
|
+
return false;
|
|
2243
|
+
}
|
|
2244
|
+
note?.(CSV_LOSS.ROLE_ASSUMED, `${domain} column "${name}" reads back with the label role (string)`, name, set);
|
|
2245
|
+
return true;
|
|
2246
|
+
}
|
|
2188
2247
|
function checkRoleColumn(column, role, names, domain, rows, note) {
|
|
2189
2248
|
if (column === null) {
|
|
2190
2249
|
return;
|
|
@@ -2223,20 +2282,7 @@ function checkValues(column, domain, rows, note, roleColumn = false) {
|
|
|
2223
2282
|
const { name, dtype } = column.meta;
|
|
2224
2283
|
const label = `${domain} column "${name}"`;
|
|
2225
2284
|
if (column.dtype === "f32" || column.dtype === "f64") {
|
|
2226
|
-
|
|
2227
|
-
const { components } = column.meta;
|
|
2228
|
-
const { data } = column;
|
|
2229
|
-
for (const row of rows) {
|
|
2230
|
-
if (!column.isSet(row)) {
|
|
2231
|
-
continue;
|
|
2232
|
-
}
|
|
2233
|
-
for (let k = 0; k < components; k++) {
|
|
2234
|
-
if (!Number.isFinite(data[row * components + k])) {
|
|
2235
|
-
nonFinite++;
|
|
2236
|
-
break;
|
|
2237
|
-
}
|
|
2238
|
-
}
|
|
2239
|
-
}
|
|
2285
|
+
const nonFinite = countNonFinite(column, rows);
|
|
2240
2286
|
if (nonFinite > 0) {
|
|
2241
2287
|
note(
|
|
2242
2288
|
CSV_LOSS.NONFINITE,
|
|
@@ -2250,15 +2296,50 @@ function checkValues(column, domain, rows, note, roleColumn = false) {
|
|
|
2250
2296
|
if (dtype !== "string" && dtype !== "dict") {
|
|
2251
2297
|
return;
|
|
2252
2298
|
}
|
|
2299
|
+
const scan = scanText(column, rows);
|
|
2300
|
+
if (roleColumn || scan.widest < 0) {
|
|
2301
|
+
return;
|
|
2302
|
+
}
|
|
2303
|
+
const readsAsDict = scan.candidate && scan.heuristic.decide() === "dict";
|
|
2304
|
+
if (scan.widest < WIDENING.string && !readsAsDict) {
|
|
2305
|
+
note(CSV_LOSS.TEXT_INFERRED, `${label}: every value reads back as ${TEXT_DTYPES[scan.widest]}`, name, scan.set);
|
|
2306
|
+
return;
|
|
2307
|
+
}
|
|
2308
|
+
if (dtype === "dict" && !readsAsDict) {
|
|
2309
|
+
note(
|
|
2310
|
+
CSV_LOSS.STORAGE_CLASS_CHANGED,
|
|
2311
|
+
`${label}: reads back as string (cardinality too high for a dict)`,
|
|
2312
|
+
name,
|
|
2313
|
+
null
|
|
2314
|
+
);
|
|
2315
|
+
} else if (dtype === "string" && readsAsDict) {
|
|
2316
|
+
note(CSV_LOSS.STORAGE_CLASS_CHANGED, `${label}: reads back as dict (low cardinality)`, name, null);
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
function countNonFinite(column, rows) {
|
|
2320
|
+
let nonFinite = 0;
|
|
2321
|
+
const { components } = column.meta;
|
|
2322
|
+
const { data } = column;
|
|
2323
|
+
for (const row of rows) {
|
|
2324
|
+
if (!column.isSet(row)) {
|
|
2325
|
+
continue;
|
|
2326
|
+
}
|
|
2327
|
+
for (let k = 0; k < components; k++) {
|
|
2328
|
+
if (!Number.isFinite(data[row * components + k])) {
|
|
2329
|
+
nonFinite++;
|
|
2330
|
+
break;
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
return nonFinite;
|
|
2335
|
+
}
|
|
2336
|
+
function scanText(column, rows) {
|
|
2253
2337
|
let widest = -1;
|
|
2254
2338
|
let candidate = true;
|
|
2255
2339
|
let set = 0;
|
|
2256
2340
|
const heuristic = new DictHeuristic(DICT_SAMPLE_ROWS);
|
|
2257
2341
|
for (const row of rows) {
|
|
2258
|
-
|
|
2259
|
-
continue;
|
|
2260
|
-
}
|
|
2261
|
-
const text = column.value(row);
|
|
2342
|
+
const text = column.isSet(row) ? column.value(row) : null;
|
|
2262
2343
|
if (typeof text !== "string") {
|
|
2263
2344
|
continue;
|
|
2264
2345
|
}
|
|
@@ -2266,30 +2347,11 @@ function checkValues(column, domain, rows, note, roleColumn = false) {
|
|
|
2266
2347
|
const kind = inferTextDtype(text);
|
|
2267
2348
|
widest = Math.max(widest, WIDENING[kind]);
|
|
2268
2349
|
if (!heuristic.decided) {
|
|
2269
|
-
|
|
2270
|
-
candidate = false;
|
|
2271
|
-
}
|
|
2350
|
+
candidate &&= kind === "string";
|
|
2272
2351
|
heuristic.observe(text);
|
|
2273
2352
|
}
|
|
2274
2353
|
}
|
|
2275
|
-
|
|
2276
|
-
return;
|
|
2277
|
-
}
|
|
2278
|
-
const readsAsDict = candidate && heuristic.decide() === "dict";
|
|
2279
|
-
if (widest < WIDENING.string && !readsAsDict) {
|
|
2280
|
-
note(CSV_LOSS.TEXT_INFERRED, `${label}: every value reads back as ${TEXT_DTYPES[widest]}`, name, set);
|
|
2281
|
-
return;
|
|
2282
|
-
}
|
|
2283
|
-
if (dtype === "dict" && !readsAsDict) {
|
|
2284
|
-
note(
|
|
2285
|
-
CSV_LOSS.STORAGE_CLASS_CHANGED,
|
|
2286
|
-
`${label}: reads back as string (cardinality too high for a dict)`,
|
|
2287
|
-
name,
|
|
2288
|
-
null
|
|
2289
|
-
);
|
|
2290
|
-
} else if (dtype === "string" && readsAsDict) {
|
|
2291
|
-
note(CSV_LOSS.STORAGE_CLASS_CHANGED, `${label}: reads back as dict (low cardinality)`, name, null);
|
|
2292
|
-
}
|
|
2354
|
+
return { widest, candidate, set, heuristic };
|
|
2293
2355
|
}
|
|
2294
2356
|
function typeText(snapshot, plan, e) {
|
|
2295
2357
|
if (!snapshot.directed || !plan.folding.sourceDirected(e)) {
|
|
@@ -2303,6 +2365,14 @@ function neighbourCell(id, weight) {
|
|
|
2303
2365
|
}
|
|
2304
2366
|
return id.includes(":") ? `${id}:` : id;
|
|
2305
2367
|
}
|
|
2368
|
+
function sourceRunEnd(src, edgeRows, r) {
|
|
2369
|
+
const source = src[edgeRows[r]];
|
|
2370
|
+
let end = r;
|
|
2371
|
+
while (end < edgeRows.length && src[edgeRows[end]] === source) {
|
|
2372
|
+
end++;
|
|
2373
|
+
}
|
|
2374
|
+
return end;
|
|
2375
|
+
}
|
|
2306
2376
|
function* adjacencyLines(snapshot, plan) {
|
|
2307
2377
|
const { delimiter, newline } = plan.csv;
|
|
2308
2378
|
const forced = (text) => `"${text.replaceAll('"', '""')}"`;
|
|
@@ -2329,10 +2399,7 @@ function* adjacencyLines(snapshot, plan) {
|
|
|
2329
2399
|
let r = 0;
|
|
2330
2400
|
while (r < edgeRows.length) {
|
|
2331
2401
|
const source = list.src[edgeRows[r]];
|
|
2332
|
-
|
|
2333
|
-
while (end < edgeRows.length && list.src[edgeRows[end]] === source) {
|
|
2334
|
-
end++;
|
|
2335
|
-
}
|
|
2402
|
+
const end = sourceRunEnd(list.src, edgeRows, r);
|
|
2336
2403
|
const fresh = [];
|
|
2337
2404
|
let highest = -1;
|
|
2338
2405
|
let inOrder = true;
|
|
@@ -2366,59 +2433,54 @@ function* adjacencyLines(snapshot, plan) {
|
|
|
2366
2433
|
yield* singles(snapshot.nodeCount - 1);
|
|
2367
2434
|
}
|
|
2368
2435
|
function* lines(snapshot, plan) {
|
|
2369
|
-
|
|
2370
|
-
if (csv.table === "adjacency") {
|
|
2436
|
+
if (plan.csv.table === "adjacency") {
|
|
2371
2437
|
yield* adjacencyLines(snapshot, plan);
|
|
2372
|
-
|
|
2438
|
+
} else if (plan.csv.table === "nodes") {
|
|
2439
|
+
yield* nodeLines(snapshot, plan);
|
|
2440
|
+
} else {
|
|
2441
|
+
yield* edgeLines(snapshot, plan);
|
|
2373
2442
|
}
|
|
2374
|
-
|
|
2443
|
+
}
|
|
2444
|
+
function cellWriters(delimiter) {
|
|
2375
2445
|
const quote = (text) => quoteCsvCell(text, delimiter);
|
|
2376
2446
|
const cell = (column, row) => {
|
|
2377
2447
|
const text = cellText(column, row);
|
|
2378
2448
|
return text === null ? "" : quote(text);
|
|
2379
2449
|
};
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
yield headers2.map(quote).join(delimiter) + newline;
|
|
2390
|
-
}
|
|
2391
|
-
for (let i = 0; i < snapshot.nodeCount; i++) {
|
|
2392
|
-
const cells = [quote(plan.idText(i))];
|
|
2393
|
-
if (plan.nodeLabel !== null) {
|
|
2394
|
-
cells.push(cell(plan.nodeLabel, i));
|
|
2395
|
-
}
|
|
2396
|
-
for (const { column } of plan.nodeColumns) {
|
|
2397
|
-
cells.push(cell(column, i));
|
|
2398
|
-
}
|
|
2399
|
-
yield cells.join(delimiter) + newline;
|
|
2400
|
-
}
|
|
2401
|
-
return;
|
|
2402
|
-
}
|
|
2403
|
-
const headers = [csv.dialect.source, csv.dialect.target];
|
|
2404
|
-
if (csv.dialect.type !== null) {
|
|
2405
|
-
headers.push(csv.dialect.type);
|
|
2406
|
-
}
|
|
2407
|
-
if (plan.edgeId !== null) {
|
|
2408
|
-
headers.push(plan.edgeId.meta.name);
|
|
2409
|
-
}
|
|
2410
|
-
if (plan.edgeLabel !== null) {
|
|
2411
|
-
headers.push(plan.edgeLabel.meta.name);
|
|
2412
|
-
}
|
|
2413
|
-
if (plan.weights.weighted) {
|
|
2414
|
-
headers.push(csv.dialect.weight);
|
|
2450
|
+
return { quote, cell };
|
|
2451
|
+
}
|
|
2452
|
+
function* nodeLines(snapshot, plan) {
|
|
2453
|
+
const { csv } = plan;
|
|
2454
|
+
const { delimiter, newline } = csv;
|
|
2455
|
+
const { quote, cell } = cellWriters(delimiter);
|
|
2456
|
+
const headers = [csv.dialect.id];
|
|
2457
|
+
if (plan.nodeLabel !== null) {
|
|
2458
|
+
headers.push(plan.nodeLabel.meta.name);
|
|
2415
2459
|
}
|
|
2416
|
-
for (const { header } of plan.
|
|
2460
|
+
for (const { header } of plan.nodeColumns) {
|
|
2417
2461
|
headers.push(header);
|
|
2418
2462
|
}
|
|
2419
2463
|
if (csv.header) {
|
|
2420
2464
|
yield headers.map(quote).join(delimiter) + newline;
|
|
2421
2465
|
}
|
|
2466
|
+
for (let i = 0; i < snapshot.nodeCount; i++) {
|
|
2467
|
+
const cells = [quote(plan.idText(i))];
|
|
2468
|
+
if (plan.nodeLabel !== null) {
|
|
2469
|
+
cells.push(cell(plan.nodeLabel, i));
|
|
2470
|
+
}
|
|
2471
|
+
for (const { column } of plan.nodeColumns) {
|
|
2472
|
+
cells.push(cell(column, i));
|
|
2473
|
+
}
|
|
2474
|
+
yield cells.join(delimiter) + newline;
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
function* edgeLines(snapshot, plan) {
|
|
2478
|
+
const { csv } = plan;
|
|
2479
|
+
const { delimiter, newline } = csv;
|
|
2480
|
+
const { quote, cell } = cellWriters(delimiter);
|
|
2481
|
+
if (csv.header) {
|
|
2482
|
+
yield edgeHeaders(plan).map(quote).join(delimiter) + newline;
|
|
2483
|
+
}
|
|
2422
2484
|
const list = snapshot.edgeList();
|
|
2423
2485
|
for (const e of plan.edgeRows) {
|
|
2424
2486
|
const cells = [quote(plan.idText(list.src[e])), quote(plan.idText(list.dst[e]))];
|
|
@@ -2440,6 +2502,26 @@ function* lines(snapshot, plan) {
|
|
|
2440
2502
|
yield cells.join(delimiter) + newline;
|
|
2441
2503
|
}
|
|
2442
2504
|
}
|
|
2505
|
+
function edgeHeaders(plan) {
|
|
2506
|
+
const { dialect } = plan.csv;
|
|
2507
|
+
const headers = [dialect.source, dialect.target];
|
|
2508
|
+
if (dialect.type !== null) {
|
|
2509
|
+
headers.push(dialect.type);
|
|
2510
|
+
}
|
|
2511
|
+
if (plan.edgeId !== null) {
|
|
2512
|
+
headers.push(plan.edgeId.meta.name);
|
|
2513
|
+
}
|
|
2514
|
+
if (plan.edgeLabel !== null) {
|
|
2515
|
+
headers.push(plan.edgeLabel.meta.name);
|
|
2516
|
+
}
|
|
2517
|
+
if (plan.weights.weighted) {
|
|
2518
|
+
headers.push(dialect.weight);
|
|
2519
|
+
}
|
|
2520
|
+
for (const { header } of plan.edgeColumns) {
|
|
2521
|
+
headers.push(header);
|
|
2522
|
+
}
|
|
2523
|
+
return headers;
|
|
2524
|
+
}
|
|
2443
2525
|
function prepare(snapshot, options) {
|
|
2444
2526
|
const plan = planExport(snapshot, options, false);
|
|
2445
2527
|
const collision = plan.notes.find((n) => n.code === CSV_LOSS.ID_TEXT_COLLISION);
|