fix(m01-sl): 가져온 표 블록 칸 차례를 정함대로 — 값 칸 범위도 아래 · 위 두 칸 · 숫자 하나면 두 칸에 같은 수
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BU9gbNE3aWFUvE61pS2WGt
This commit is contained in:
@@ -98,8 +98,8 @@ const cellOf = (v: unknown): Cell | null =>
|
||||
interface TableBox {
|
||||
node: SlNode;
|
||||
table: SlImportTable;
|
||||
/** 결과 칸 이름 → 표 열 글자 */
|
||||
colOf: Map<string, string>;
|
||||
/** 결과 칸 이름 → 표 열 글자(범위 칸은 아래 · 위) */
|
||||
colOf: Map<string, { x: string; hi?: string }>;
|
||||
rows: number;
|
||||
/** 다음 빈 열(맞음 · 결과 셈 칸) */
|
||||
free: number;
|
||||
@@ -442,28 +442,32 @@ function newTable(b: Build, n: Extract<Ast, { t: "find" }>, sig: string): TableB
|
||||
};
|
||||
}
|
||||
}
|
||||
const colOf = new Map<string, string>();
|
||||
for (const c of cols) if (c.part === "v") colOf.set(c.key, colName(4 + cols.indexOf(c)));
|
||||
const colOf = new Map<string, { x: string; hi?: string }>();
|
||||
cols.forEach((c, j) => {
|
||||
if (c.part === "hi") colOf.set(c.key, { ...colOf.get(c.key)!, hi: colName(4 + j) });
|
||||
else if (!colOf.has(c.key)) colOf.set(c.key, { x: colName(4 + j) });
|
||||
});
|
||||
const box: TableBox = { node, table, colOf, rows: N, free, hit };
|
||||
b.tables.set(sig, box);
|
||||
return box;
|
||||
}
|
||||
|
||||
/** 결과 알약 — 그 칸이 빈 줄은 빼고(여러 줄일 때) 하나만 맞으면 INDEX/MATCH · 아니면 #N/A */
|
||||
/** 결과 알약 — 그 칸이 빈 줄은 빼고(여러 줄일 때) 하나만 맞으면 INDEX/MATCH · 아니면 #N/A ·
|
||||
* 범위 값 칸은 아래 = 위(숫자 하나)일 때만 값 · 아니면 #N/A(엔진 「값이 범위」) */
|
||||
function resultCol(b: Build, box: TableBox, col: string): void {
|
||||
const s = b.sheet(box.node);
|
||||
const N = box.rows;
|
||||
const range = (L: string): string => `$${L}$2:$${L}$${N + 1}`;
|
||||
const X = box.colOf.get(col)!;
|
||||
const { x: X, hi } = box.colOf.get(col)!;
|
||||
const W = colName(box.free++);
|
||||
s.칸[`${W}1`] = { 값: `${t("Match")} · ${col}` };
|
||||
for (let r = 2; r <= N + 1; r++)
|
||||
s.칸[`${W}${r}`] = {
|
||||
식: `IF(AND(${box.hit}${r}=1,OR(COUNTIF(${range(box.hit)},1)<2,${X}${r}<>"")),1,0)`,
|
||||
};
|
||||
b.out(box.node, col, {
|
||||
식: `IF(COUNTIF(${range(W)},1)=1,INDEX(${range(X)},MATCH(1,${range(W)},0)),NA())`,
|
||||
});
|
||||
const at = (L: string): string => `INDEX(${range(L)},MATCH(1,${range(W)},0))`;
|
||||
const value = hi ? `IF(${at(X)}=${at(hi)},${at(X)},NA())` : at(X);
|
||||
b.out(box.node, col, { 식: `IF(COUNTIF(${range(W)},1)=1,${value},NA())` });
|
||||
}
|
||||
|
||||
/* ── 상자들 ─────────────────────────────────────────────────────────────── */
|
||||
|
||||
@@ -152,32 +152,35 @@ const LOW_HIGH = currentLanguageIndex === 1 ? ["low", "high"] : ["아래", "위"
|
||||
|
||||
export const RULES = ["초과·이하", "이상·이하", "이상·미만", "이하"];
|
||||
|
||||
/** 표 칸 놓기 — 조건 칸 차례(범위는 아래 · 위 두 칸) → 값 칸 차례 */
|
||||
/** 표 칸 놓기(서버 `M01_SL_Router._table` 과 같음 · 정함) — 조건 칸 → 값 칸 차례 · 범위(조건 「범위」 · 값 칸에
|
||||
* 「20~45」 꼴이 한 줄이라도 있음)는 「이름·아래」 「이름·위」 두 칸 · 범위 칸에 숫자 하나면 두 칸에 같은 수 */
|
||||
export function tableCols(
|
||||
table: SlImportTable,
|
||||
): { key: string; head: string; part: "v" | "lo" | "hi" }[] {
|
||||
const out: { key: string; head: string; part: "v" | "lo" | "hi" }[] = [];
|
||||
for (const [k, kind] of Object.entries(table.조건)) {
|
||||
if (kind === "범위") {
|
||||
const conds = Object.keys(table.조건);
|
||||
for (const k of [...conds, ...Object.keys(table.값칸)]) {
|
||||
const split = conds.includes(k)
|
||||
? table.조건[k] === "범위"
|
||||
: table.줄.some((r) => Array.isArray(r[k]));
|
||||
if (split) {
|
||||
out.push({ key: k, head: `${k}·${LOW_HIGH[0]}`, part: "lo" });
|
||||
out.push({ key: k, head: `${k}·${LOW_HIGH[1]}`, part: "hi" });
|
||||
} else out.push({ key: k, head: k, part: "v" });
|
||||
}
|
||||
for (const k of Object.keys(table.값칸))
|
||||
if (!(k in table.조건)) out.push({ key: k, head: k, part: "v" });
|
||||
return out;
|
||||
}
|
||||
|
||||
/** 품셈 표 → 시트 칸(E1 부터 · 머리 줄 + 줄) — 바깥 값 모양(`M01_SL_Api` 「표」)과 같음 */
|
||||
/** 품셈 표 → 시트 칸(E1 부터 · 머리 줄 + 줄) — 바깥 값 「표」 답(`rows`)과 같음 */
|
||||
export function tableRows(table: SlImportTable): (string | number | null)[][] {
|
||||
const cols = tableCols(table);
|
||||
const one = (v: unknown, part: "v" | "lo" | "hi"): string | number | null => {
|
||||
if (Array.isArray(v)) {
|
||||
if (part === "v") return v.map((x) => x ?? "").join("~");
|
||||
const x = v[part === "lo" ? 0 : 1];
|
||||
if (part !== "v") {
|
||||
const x = Array.isArray(v) ? v[part === "lo" ? 0 : 1] : v;
|
||||
return typeof x === "number" ? x : null;
|
||||
}
|
||||
return typeof v === "number" || typeof v === "string" ? v : null;
|
||||
if (Array.isArray(v)) return v.map((x) => x ?? "").join(",");
|
||||
return typeof v === "number" || (typeof v === "string" && v !== "") ? v : null;
|
||||
};
|
||||
return [cols.map((c) => c.head), ...table.줄.map((r) => cols.map((c) => one(r[c.key], c.part)))];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user