@trebco/treb 28.13.2 → 28.15.1
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/dist/treb-spreadsheet-light.mjs +9 -9
- package/dist/treb-spreadsheet.mjs +15 -15
- package/dist/treb.d.ts +19 -3
- package/package.json +1 -1
- package/treb-base-types/src/style.ts +3 -0
- package/treb-calculator/src/expression-calculator.ts +9 -5
- package/treb-calculator/src/functions/base-functions.ts +405 -57
- package/treb-calculator/src/utilities.ts +37 -24
- package/treb-embed/markup/layout.html +15 -10
- package/treb-embed/markup/toolbar.html +5 -5
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +38 -2
- package/treb-embed/src/embedded-spreadsheet.ts +66 -2
- package/treb-embed/src/options.ts +5 -0
- package/treb-embed/style/formula-bar.scss +20 -7
- package/treb-embed/style/theme-defaults.scss +20 -0
- package/treb-export/src/export2.ts +6 -1
- package/treb-export/src/import2.ts +66 -13
- package/treb-export/src/shared-strings2.ts +1 -1
- package/treb-export/src/workbook-style2.ts +25 -64
- package/treb-export/src/workbook2.ts +119 -1
- package/treb-grid/src/editors/formula_bar.ts +23 -1
- package/treb-grid/src/render/tile_renderer.ts +48 -3
- package/treb-grid/src/types/annotation.ts +17 -3
- package/treb-grid/src/types/grid.ts +9 -1
- package/treb-grid/src/types/grid_options.ts +3 -2
- package/treb-grid/src/types/named_range.ts +8 -1
package/dist/treb.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! API v28.
|
|
1
|
+
/*! API v28.15. Copyright 2018-2024 trebco, llc. All rights reserved. LGPL: https://treb.app/license */
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* add our tag to the map
|
|
@@ -1077,7 +1077,7 @@ export interface FreezePane {
|
|
|
1077
1077
|
rows: number;
|
|
1078
1078
|
columns: number;
|
|
1079
1079
|
}
|
|
1080
|
-
export type AnnotationType = 'treb-chart' | 'image' | 'external';
|
|
1080
|
+
export type AnnotationType = 'treb-chart' | 'image' | 'textbox' | 'external';
|
|
1081
1081
|
export declare type BorderConstants = "none" | "all" | "outside" | "top" | "bottom" | "left" | "right";
|
|
1082
1082
|
|
|
1083
1083
|
/**
|
|
@@ -1274,6 +1274,9 @@ export interface CellStyle {
|
|
|
1274
1274
|
/** border color */
|
|
1275
1275
|
border_bottom_fill?: Color;
|
|
1276
1276
|
|
|
1277
|
+
/** text indent */
|
|
1278
|
+
indent?: number;
|
|
1279
|
+
|
|
1277
1280
|
/**
|
|
1278
1281
|
* cell is locked for editing
|
|
1279
1282
|
*/
|
|
@@ -1805,7 +1808,7 @@ export interface SerializedGridSelection {
|
|
|
1805
1808
|
/** for cacheing addtional selections. optimally don't serialize */
|
|
1806
1809
|
rendered?: boolean;
|
|
1807
1810
|
}
|
|
1808
|
-
export type AnnotationData = AnnotationChartData | AnnotationImageData | AnnotationExternalData;
|
|
1811
|
+
export type AnnotationData = AnnotationChartData | AnnotationImageData | AnnotationExternalData | AnnotationTextBoxData;
|
|
1809
1812
|
export interface ImageSize {
|
|
1810
1813
|
width: number;
|
|
1811
1814
|
height: number;
|
|
@@ -1888,6 +1891,19 @@ export interface AnnotationImageData extends AnnotationDataBase {
|
|
|
1888
1891
|
export interface AnnotationChartData extends AnnotationDataBase {
|
|
1889
1892
|
type: 'treb-chart';
|
|
1890
1893
|
}
|
|
1894
|
+
export interface AnnotationTextBoxData extends AnnotationDataBase {
|
|
1895
|
+
type: 'textbox';
|
|
1896
|
+
data: {
|
|
1897
|
+
style?: CellStyle;
|
|
1898
|
+
paragraphs: {
|
|
1899
|
+
style?: CellStyle;
|
|
1900
|
+
content: {
|
|
1901
|
+
text: string;
|
|
1902
|
+
style?: CellStyle;
|
|
1903
|
+
}[];
|
|
1904
|
+
}[];
|
|
1905
|
+
};
|
|
1906
|
+
}
|
|
1891
1907
|
export interface AnnotationExternalData extends AnnotationDataBase {
|
|
1892
1908
|
type: 'external';
|
|
1893
1909
|
data: Record<string, string>;
|
package/package.json
CHANGED
|
@@ -806,15 +806,11 @@ export class ExpressionCalculator {
|
|
|
806
806
|
|
|
807
807
|
}
|
|
808
808
|
|
|
809
|
-
//protected ElementwiseBinaryExpression(fn: Primitives.PrimitiveBinaryExpression, left: UnionValue[][], right: UnionValue[][]): UnionValue[][] {
|
|
810
809
|
protected ElementwiseBinaryExpression(fn: Primitives.PrimitiveBinaryExpression, left: ArrayUnion, right: ArrayUnion): ArrayUnion {
|
|
811
810
|
|
|
812
811
|
const columns = Math.max(left.value.length, right.value.length);
|
|
813
812
|
const rows = Math.max(left.value[0].length, right.value[0].length);
|
|
814
813
|
|
|
815
|
-
// const columns = Math.max(left.length, right.length);
|
|
816
|
-
// const rows = Math.max(left[0].length, right[0].length);
|
|
817
|
-
|
|
818
814
|
const left_values = this.RecycleArray(left.value, columns, rows);
|
|
819
815
|
const right_values = this.RecycleArray(right.value, columns, rows);
|
|
820
816
|
|
|
@@ -822,8 +818,16 @@ export class ExpressionCalculator {
|
|
|
822
818
|
|
|
823
819
|
for (let c = 0; c < columns; c++) {
|
|
824
820
|
const col: UnionValue[] = [];
|
|
821
|
+
|
|
825
822
|
for (let r = 0; r < rows; r++ ) {
|
|
826
|
-
|
|
823
|
+
|
|
824
|
+
// handle undefineds. this is unfortunate. shouldn't the recycle
|
|
825
|
+
// function do that? ...CHECK/TODO/FIXME
|
|
826
|
+
|
|
827
|
+
col[r] = fn(
|
|
828
|
+
left_values[c][r] || { type: ValueType.undefined },
|
|
829
|
+
right_values[c][r] || { type: ValueType.undefined });
|
|
830
|
+
|
|
827
831
|
}
|
|
828
832
|
value.push(col);
|
|
829
833
|
}
|
|
@@ -88,6 +88,98 @@ const inverse_normal = (q: number): number => {
|
|
|
88
88
|
|
|
89
89
|
};
|
|
90
90
|
|
|
91
|
+
const zlookup_arguments = [
|
|
92
|
+
{
|
|
93
|
+
name: "Lookup value",
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: "Table",
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: "Result index",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: "Inexact",
|
|
103
|
+
default: true,
|
|
104
|
+
},
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* unified VLOOKUP/HLOOKUP. ordinarily we'd call it XLOOKUP but that's taken.
|
|
109
|
+
* FIXME: can't use use that function for this?
|
|
110
|
+
*/
|
|
111
|
+
const ZLookup = (value: any, table: any[][], col: number, inexact = true, transpose = false): UnionValue => {
|
|
112
|
+
|
|
113
|
+
if (transpose) {
|
|
114
|
+
table = Utils.TransposeArray(table);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
col = Math.max(0, col - 1);
|
|
118
|
+
|
|
119
|
+
// inexact is the default. this assumes that the data is sorted,
|
|
120
|
+
// either numerically or alphabetically. it returns the closest
|
|
121
|
+
// value without going over -- meaning walk the list, and when
|
|
122
|
+
// you're over return the _previous_ item. except if there's an
|
|
123
|
+
// exact match, I guess, in that case return the exact match.
|
|
124
|
+
|
|
125
|
+
// FIXME: there's a hint in the docs for XLOOKUP that this might
|
|
126
|
+
// be using a binary search. not sure why, but that might be
|
|
127
|
+
// correct.
|
|
128
|
+
|
|
129
|
+
if (inexact) {
|
|
130
|
+
|
|
131
|
+
let result: any = table[col][0];
|
|
132
|
+
|
|
133
|
+
if (typeof value === 'number') {
|
|
134
|
+
|
|
135
|
+
let compare = Number(table[0][0]);
|
|
136
|
+
if (isNaN(compare) || compare > value) {
|
|
137
|
+
return NAError();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
for (let i = 1; i < table[0].length; i++) {
|
|
141
|
+
compare = Number(table[0][i]);
|
|
142
|
+
if (isNaN(compare) || compare > value) {
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
result = table[col][i];
|
|
146
|
+
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
|
|
152
|
+
value = value.toLowerCase(); // ?
|
|
153
|
+
let compare: string = (table[0][0] || '').toString().toLowerCase();
|
|
154
|
+
if (compare.localeCompare(value) > 0) {
|
|
155
|
+
return NAError();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
for (let i = 1; i < table[0].length; i++) {
|
|
159
|
+
compare = (table[0][i] || '').toString().toLowerCase();
|
|
160
|
+
if (compare.localeCompare(value) > 0) {
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
result = table[col][i];
|
|
164
|
+
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return Box(result);
|
|
170
|
+
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
for (let i = 0; i < table[0].length; i++) {
|
|
174
|
+
if (table[0][i] == value) { // ==
|
|
175
|
+
return Box(table[col][i]);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return NAError();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
};
|
|
182
|
+
|
|
91
183
|
/**
|
|
92
184
|
* alternate functions. these are used (atm) only for changing complex
|
|
93
185
|
* behavior.
|
|
@@ -637,6 +729,77 @@ export const BaseFunctionLibrary: FunctionMap = {
|
|
|
637
729
|
})
|
|
638
730
|
},
|
|
639
731
|
|
|
732
|
+
Large: {
|
|
733
|
+
description: 'Returns the nth numeric value from the data, in descending order',
|
|
734
|
+
arguments: [
|
|
735
|
+
{
|
|
736
|
+
name: 'values',
|
|
737
|
+
},
|
|
738
|
+
{
|
|
739
|
+
name: 'index',
|
|
740
|
+
}
|
|
741
|
+
],
|
|
742
|
+
|
|
743
|
+
// OK a little tricky here -- we're going to reverse the arguments
|
|
744
|
+
// so we can call apply as array, but applying against the trailing
|
|
745
|
+
// arguments.
|
|
746
|
+
|
|
747
|
+
fn: Utils.ApplyAsArraySwap((data: any, index: number) => {
|
|
748
|
+
|
|
749
|
+
if (index <= 0) {
|
|
750
|
+
return ArgumentError();
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
const flat = Utils.FlattenUnboxed(data);
|
|
754
|
+
const numeric: number[] = flat.filter(test => typeof test === 'number');
|
|
755
|
+
numeric.sort((a, b) => b - a);
|
|
756
|
+
|
|
757
|
+
if (index <= numeric.length) {
|
|
758
|
+
return {
|
|
759
|
+
type: ValueType.number,
|
|
760
|
+
value: numeric[index - 1],
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
return ArgumentError();
|
|
765
|
+
}),
|
|
766
|
+
},
|
|
767
|
+
|
|
768
|
+
Small: {
|
|
769
|
+
description: 'Returns the nth numeric value from the data, in ascending order',
|
|
770
|
+
arguments: [
|
|
771
|
+
{
|
|
772
|
+
name: 'values',
|
|
773
|
+
},
|
|
774
|
+
{
|
|
775
|
+
name: 'index',
|
|
776
|
+
}
|
|
777
|
+
],
|
|
778
|
+
|
|
779
|
+
// see large, above
|
|
780
|
+
|
|
781
|
+
fn: Utils.ApplyAsArraySwap((data: any, index: number) => {
|
|
782
|
+
|
|
783
|
+
if (index <= 0) {
|
|
784
|
+
return ArgumentError();
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
const flat = Utils.FlattenUnboxed(data);
|
|
788
|
+
const numeric: number[] = flat.filter(test => typeof test === 'number');
|
|
789
|
+
numeric.sort((a, b) => a - b);
|
|
790
|
+
|
|
791
|
+
if (index <= numeric.length) {
|
|
792
|
+
return {
|
|
793
|
+
type: ValueType.number,
|
|
794
|
+
value: numeric[index - 1],
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
return ArgumentError();
|
|
799
|
+
|
|
800
|
+
}),
|
|
801
|
+
},
|
|
802
|
+
|
|
640
803
|
/**
|
|
641
804
|
* sort arguments, but ensure we return empty strings to
|
|
642
805
|
* fill up the result array
|
|
@@ -803,90 +966,275 @@ export const BaseFunctionLibrary: FunctionMap = {
|
|
|
803
966
|
},
|
|
804
967
|
*/
|
|
805
968
|
|
|
806
|
-
|
|
807
|
-
*
|
|
808
|
-
*
|
|
969
|
+
/*
|
|
970
|
+
* unsaid anywhere (that I can locate) aboud XLOOKUP is that lookup
|
|
971
|
+
* array must be one-dimensional. it can be either a row or a column,
|
|
972
|
+
* but one dimension must be one. that simplifies things quite a bit.
|
|
973
|
+
*
|
|
974
|
+
* there's a note in the docs about binary search over the data --
|
|
975
|
+
* that might explain how inexact VLOOKUP works as well. seems an odd
|
|
976
|
+
* choice but maybe back in the day it made sense
|
|
809
977
|
*/
|
|
810
|
-
|
|
811
|
-
|
|
978
|
+
XLOOKUP: {
|
|
812
979
|
arguments: [
|
|
813
|
-
{
|
|
814
|
-
|
|
815
|
-
},
|
|
816
|
-
{
|
|
817
|
-
|
|
818
|
-
},
|
|
819
|
-
{
|
|
820
|
-
name: "Result index",
|
|
821
|
-
},
|
|
822
|
-
{
|
|
823
|
-
name: "Inexact",
|
|
824
|
-
default: true,
|
|
825
|
-
},
|
|
980
|
+
{ name: 'Lookup value', },
|
|
981
|
+
{ name: 'Lookup array', },
|
|
982
|
+
{ name: 'Return array', },
|
|
983
|
+
{ name: 'Not found', boxed: true },
|
|
984
|
+
{ name: 'Match mode', default: 0, },
|
|
985
|
+
{ name: 'Search mode', default: 1, },
|
|
826
986
|
],
|
|
987
|
+
xlfn: true,
|
|
988
|
+
fn: (
|
|
989
|
+
lookup_value: any,
|
|
990
|
+
lookup_array: any[][],
|
|
991
|
+
return_array: any[][],
|
|
992
|
+
not_found?: UnionValue,
|
|
993
|
+
match_mode = 0,
|
|
994
|
+
search_mode = 1,
|
|
995
|
+
): UnionValue => {
|
|
996
|
+
|
|
997
|
+
// FIXME: we could I suppose be more graceful about single values
|
|
998
|
+
// if passed instead of arrays
|
|
999
|
+
|
|
1000
|
+
if (!Array.isArray(lookup_array)) {
|
|
1001
|
+
console.info("lookup is not an array");
|
|
1002
|
+
return ValueError();
|
|
1003
|
+
}
|
|
827
1004
|
|
|
828
|
-
|
|
1005
|
+
const first = lookup_array[0];
|
|
1006
|
+
if (!Array.isArray(first)) {
|
|
1007
|
+
console.info("lookip is not a 2d array");
|
|
1008
|
+
return ValueError();
|
|
1009
|
+
}
|
|
829
1010
|
|
|
830
|
-
|
|
1011
|
+
if (lookup_array.length !== 1 && first.length !== 1) {
|
|
1012
|
+
console.info("lookup array has invalid dimensions");
|
|
1013
|
+
return ValueError();
|
|
1014
|
+
}
|
|
831
1015
|
|
|
832
|
-
//
|
|
833
|
-
//
|
|
834
|
-
// value without going over -- meaning walk the list, and when
|
|
835
|
-
// you're over return the _previous_ item. except if there's an
|
|
836
|
-
// exact match, I guess, in that case return the exact match.
|
|
837
|
-
|
|
838
|
-
if (inexact) {
|
|
1016
|
+
// FIXME: is it required that the return array be (at least) the
|
|
1017
|
+
// same size? we can return undefineds, but maybe we should error
|
|
839
1018
|
|
|
840
|
-
|
|
1019
|
+
if (!Array.isArray(return_array)) {
|
|
1020
|
+
console.info("return array is not an array");
|
|
1021
|
+
return ValueError();
|
|
1022
|
+
}
|
|
841
1023
|
|
|
842
|
-
|
|
1024
|
+
let transpose = (lookup_array.length === 1);
|
|
1025
|
+
if (transpose) {
|
|
1026
|
+
lookup_array = Utils.TransposeArray(lookup_array);
|
|
1027
|
+
return_array = Utils.TransposeArray(return_array);
|
|
1028
|
+
}
|
|
843
1029
|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
1030
|
+
// maybe reverse...
|
|
1031
|
+
|
|
1032
|
+
if (search_mode < 0) {
|
|
1033
|
+
lookup_array.reverse();
|
|
1034
|
+
return_array.reverse();
|
|
1035
|
+
}
|
|
848
1036
|
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
1037
|
+
//
|
|
1038
|
+
// return value at index, transpose if necessary, and return
|
|
1039
|
+
// an array. we might prefer to return a scalar if there's only
|
|
1040
|
+
// one value, not sure what's the intended behavior
|
|
1041
|
+
//
|
|
1042
|
+
const ReturnIndex = (index: number): UnionValue => {
|
|
1043
|
+
|
|
1044
|
+
const values = return_array[index];
|
|
1045
|
+
|
|
1046
|
+
if (!values) {
|
|
1047
|
+
return { type: ValueType.undefined };
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
if (!Array.isArray(values)) {
|
|
1051
|
+
return Box(values);
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
let boxes = [values.map(value => Box(value))];
|
|
1055
|
+
|
|
1056
|
+
if (transpose) {
|
|
1057
|
+
boxes = Utils.TransposeArray(boxes);
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
return {
|
|
1061
|
+
type: ValueType.array,
|
|
1062
|
+
value: boxes,
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
};
|
|
1066
|
+
|
|
1067
|
+
// if value is not a string, then we can ignore wildcards.
|
|
1068
|
+
// in that case convert to exact match.
|
|
1069
|
+
|
|
1070
|
+
if (match_mode === 2 && typeof lookup_value !== 'string') {
|
|
1071
|
+
match_mode = 0;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// what does inexact matching mean in this case if the lookup
|
|
1075
|
+
// value is a string or boolean? (...)
|
|
1076
|
+
|
|
1077
|
+
if ((match_mode === 1 || match_mode === -1) && typeof lookup_value === 'number') {
|
|
1078
|
+
|
|
1079
|
+
let min_delta = 0;
|
|
1080
|
+
let index = -1;
|
|
1081
|
+
|
|
1082
|
+
for (let i = 0; i < lookup_array.length; i++) {
|
|
1083
|
+
const value = lookup_array[i][0];
|
|
1084
|
+
|
|
1085
|
+
|
|
1086
|
+
if (typeof value === 'number') {
|
|
1087
|
+
|
|
1088
|
+
// check for exact match first, just in case
|
|
1089
|
+
if (value === lookup_value) {
|
|
1090
|
+
return ReturnIndex(i);
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
const delta = Math.abs(value - lookup_value);
|
|
1094
|
+
|
|
1095
|
+
if ((match_mode === 1 && value > lookup_value) || (match_mode === -1 && value < lookup_value)){
|
|
1096
|
+
if (index < 0 || delta < min_delta) {
|
|
1097
|
+
min_delta = delta;
|
|
1098
|
+
index = i;
|
|
1099
|
+
}
|
|
853
1100
|
}
|
|
854
|
-
result = table[col][i];
|
|
855
1101
|
|
|
856
1102
|
}
|
|
1103
|
+
}
|
|
857
1104
|
|
|
1105
|
+
if (index >= 0) {
|
|
1106
|
+
return ReturnIndex(index);
|
|
858
1107
|
}
|
|
859
|
-
else {
|
|
860
1108
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
switch (match_mode) {
|
|
1112
|
+
|
|
1113
|
+
case 2:
|
|
1114
|
+
{
|
|
1115
|
+
// wildcard string match. we only handle strings for
|
|
1116
|
+
// this case (see above).
|
|
866
1117
|
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
1118
|
+
const pattern = Utils.ParseWildcards(lookup_value);
|
|
1119
|
+
const regex = new RegExp('^' + pattern + '$', 'i'); //.exec(lookup_value);
|
|
1120
|
+
|
|
1121
|
+
for (let i = 0; i < lookup_array.length; i++) {
|
|
1122
|
+
let value = lookup_array[i][0];
|
|
1123
|
+
if (typeof value === 'string' && regex.exec(value)) {
|
|
1124
|
+
return ReturnIndex(i);
|
|
1125
|
+
}
|
|
871
1126
|
}
|
|
872
|
-
result = table[col][i];
|
|
873
1127
|
|
|
874
1128
|
}
|
|
1129
|
+
break;
|
|
875
1130
|
|
|
876
|
-
|
|
1131
|
+
case 0:
|
|
1132
|
+
if (typeof lookup_value === 'string') {
|
|
1133
|
+
lookup_value = lookup_value.toLowerCase();
|
|
1134
|
+
}
|
|
1135
|
+
for (let i = 0; i < lookup_array.length; i++) {
|
|
1136
|
+
let value = lookup_array[i][0];
|
|
1137
|
+
|
|
1138
|
+
if (typeof value === 'string') {
|
|
1139
|
+
value = value.toLowerCase();
|
|
1140
|
+
}
|
|
1141
|
+
if (value === lookup_value) {
|
|
1142
|
+
return ReturnIndex(i);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
877
1145
|
|
|
878
|
-
|
|
1146
|
+
break;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
/*
|
|
1150
|
+
const flat_lookup = Utils.FlattenUnboxed(lookup_array);
|
|
1151
|
+
const flat_return = Utils.FlattenUnboxed(return_array);
|
|
879
1152
|
|
|
1153
|
+
// maybe reverse...
|
|
1154
|
+
|
|
1155
|
+
if (search_mode < 0) {
|
|
1156
|
+
flat_lookup.reverse();
|
|
1157
|
+
flat_return.reverse();
|
|
880
1158
|
}
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
1159
|
+
|
|
1160
|
+
// if value is not a string, then we can ignore wildcards.
|
|
1161
|
+
// in that case convert to exact match.
|
|
1162
|
+
|
|
1163
|
+
if (match_mode === 2 && typeof lookup_value !== 'string') {
|
|
1164
|
+
match_mode = 0;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
switch (match_mode) {
|
|
1168
|
+
case 2:
|
|
1169
|
+
|
|
1170
|
+
{
|
|
1171
|
+
|
|
1172
|
+
// wildcard string match. we only handle strings
|
|
1173
|
+
// for wildcard matching (handled above).
|
|
1174
|
+
|
|
1175
|
+
const pattern = Utils.ParseWildcards(lookup_value);
|
|
1176
|
+
const regex = new RegExp('^' + pattern + '$', 'i'); //.exec(lookup_value);
|
|
1177
|
+
|
|
1178
|
+
for (let i = 0; i < flat_lookup.length; i++) {
|
|
1179
|
+
let value = flat_lookup[i];
|
|
1180
|
+
if (typeof value === 'string' && regex.exec(value)) {
|
|
1181
|
+
return Box(flat_return[i]);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
|
|
885
1186
|
}
|
|
886
|
-
|
|
887
|
-
|
|
1187
|
+
|
|
1188
|
+
break;
|
|
1189
|
+
|
|
1190
|
+
case 0:
|
|
1191
|
+
|
|
1192
|
+
// return exact match or NA/default. in this case
|
|
1193
|
+
// "exact" means icase (but not wildcard)
|
|
1194
|
+
|
|
1195
|
+
if (typeof lookup_value === 'string') {
|
|
1196
|
+
lookup_value = lookup_value.toLowerCase();
|
|
1197
|
+
}
|
|
1198
|
+
for (let i = 0; i < flat_lookup.length; i++) {
|
|
1199
|
+
let value = flat_lookup[i];
|
|
1200
|
+
if (typeof value === 'string') {
|
|
1201
|
+
value = value.toLowerCase();
|
|
1202
|
+
}
|
|
1203
|
+
if (value === lookup_value) {
|
|
1204
|
+
return Box(flat_return[i]);
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
break;
|
|
888
1209
|
}
|
|
1210
|
+
*/
|
|
1211
|
+
|
|
1212
|
+
// FIXME: if we're expecting to return an array maybe we should
|
|
1213
|
+
// pack it up as an array? if it's not already an array? (...)
|
|
1214
|
+
|
|
1215
|
+
return (not_found && not_found.type !== ValueType.undefined) ? not_found : NAError();
|
|
1216
|
+
|
|
1217
|
+
},
|
|
1218
|
+
},
|
|
889
1219
|
|
|
1220
|
+
/**
|
|
1221
|
+
* copied from HLOOKUP, fix that one first
|
|
1222
|
+
*/
|
|
1223
|
+
HLookup: {
|
|
1224
|
+
arguments: [...zlookup_arguments],
|
|
1225
|
+
fn: (value: any, table: any[][], col: number, inexact = true): UnionValue => {
|
|
1226
|
+
return ZLookup(value, table, col, inexact, true);
|
|
1227
|
+
},
|
|
1228
|
+
},
|
|
1229
|
+
|
|
1230
|
+
/**
|
|
1231
|
+
* FIXME: does not implement inexact matching (what's the algo for
|
|
1232
|
+
* that, anyway? nearest? price is right style? what about ties?)
|
|
1233
|
+
*/
|
|
1234
|
+
VLookup: {
|
|
1235
|
+
arguments: [...zlookup_arguments],
|
|
1236
|
+
fn: (value: any, table: any[][], col: number, inexact = true): UnionValue => {
|
|
1237
|
+
return ZLookup(value, table, col, inexact, false);
|
|
890
1238
|
},
|
|
891
1239
|
},
|
|
892
1240
|
|
|
@@ -193,22 +193,52 @@ export const ApplyArrayFunc = (base: (...args: any[]) => any) => {
|
|
|
193
193
|
};
|
|
194
194
|
};
|
|
195
195
|
|
|
196
|
+
export const ApplyAsArraySwap = (base: (...args: any[]) => UnionValue) => {
|
|
197
|
+
return (...args: any[]): UnionValue => {
|
|
198
|
+
|
|
199
|
+
// swap here
|
|
200
|
+
args.reverse();
|
|
201
|
+
const [a, ...rest] = args;
|
|
202
|
+
|
|
203
|
+
if (Array.isArray(a)) {
|
|
204
|
+
return {
|
|
205
|
+
type: ValueType.array,
|
|
206
|
+
value: a.map(row => row.map((element: any) => {
|
|
207
|
+
|
|
208
|
+
// swap back
|
|
209
|
+
const swapped = [...rest, element];
|
|
210
|
+
return base(...swapped);
|
|
211
|
+
|
|
212
|
+
})),
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
else if (typeof a === 'object' && !!a && a.type === ValueType.array ) {
|
|
216
|
+
return {
|
|
217
|
+
type: ValueType.array,
|
|
218
|
+
value: (a as ArrayUnion).value.map(row => row.map((element: any) => {
|
|
219
|
+
|
|
220
|
+
const swapped = [...rest, element];
|
|
221
|
+
return base(...swapped);
|
|
222
|
+
|
|
223
|
+
})),
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
return base(...rest, a);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
196
233
|
export const ApplyAsArray = (base: (a: any, ...rest: any[]) => UnionValue) => {
|
|
197
234
|
return (a: any, ...rest: any[]): UnionValue => {
|
|
198
235
|
if (Array.isArray(a)) {
|
|
199
|
-
|
|
200
236
|
return {
|
|
201
237
|
type: ValueType.array,
|
|
202
238
|
value: a.map(row => row.map((element: any) => {
|
|
203
239
|
return base(element, ...rest);
|
|
204
240
|
})),
|
|
205
241
|
};
|
|
206
|
-
|
|
207
|
-
/*
|
|
208
|
-
return a.map(row => row.map((element: any) => {
|
|
209
|
-
return base(element, ...rest);
|
|
210
|
-
}));
|
|
211
|
-
*/
|
|
212
242
|
}
|
|
213
243
|
else if (typeof a === 'object' && !!a && a.type === ValueType.array ) {
|
|
214
244
|
return {
|
|
@@ -280,23 +310,6 @@ export const ApplyAsArray2 = (base: (a: any, b: any, ...rest: any[]) => UnionVal
|
|
|
280
310
|
};
|
|
281
311
|
}
|
|
282
312
|
|
|
283
|
-
/*
|
|
284
|
-
if (Array.isArray(a)) {
|
|
285
|
-
if (Array.isArray(b)) {
|
|
286
|
-
return a.map((row, i: number) => row.map((element: any, j: number) => {
|
|
287
|
-
return base(element, b[i][j], ...rest);
|
|
288
|
-
}));
|
|
289
|
-
}
|
|
290
|
-
else {
|
|
291
|
-
return a.map(row => row.map((element: any) => {
|
|
292
|
-
return base(element, b, ...rest);
|
|
293
|
-
}));
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
else {
|
|
297
|
-
return base(a, b, ...rest);
|
|
298
|
-
}
|
|
299
|
-
*/
|
|
300
313
|
return base(a, b, ...rest);
|
|
301
314
|
|
|
302
315
|
}
|