Excel:如何将表格转换为记录结构列表

Mor*_*aki 5 worksheet-function microsoft-excel

给定下表,我想找到满足输入x值和给定容差的最高标头编号(由 中 的值J4表示)和伴随的行索引(由 中y的值表示J5):I2J2

正如您所看到的,表中有两个值满足输入值和容差:cellsF3G3。然而,由于 中的标题行号G2(48) 高于F2(44),因此我们将作为最终解决方案,并打印出和G3中各自的数字。J4J5

我有一个解决方案,使用具有绝对值的中间矩阵并减去输入值。G然后在一个冗长的公式中使用 IF 和 SMALL向后遍历每一行B(非常有限且相当硬编码)。

我的愿望是使用现代 Excel 函数(LAMBDA、MAP 等)将该表转换为记录列表,然后简单地输出相应的值。本质上,将表转换为内存中的记录结构,而工作表中没有中间矩阵,如下所示(G2G3和 的转换示例E7):

List<Record> vals = [ 
   (36.22, 48, 1),
   (69.31, 48, 2),
   ...
   (169.99, 40, 6),
   ...
]
Run Code Online (Sandbox Code Playgroud)

使用此结构,可以更轻松地找到输入值和容差范围内的第一个值,并输出 x和 yvals.[field1]的相应列表条目。vals.[field2]vals.[field3]

使用现代 Excel 函数和内存计算可以优雅地完成此操作吗?

输入数据可以在这里找到: https: //pastebin.com/JRaA14e8

28 32 36 40 44 48
1 23.02 25.66 28.30 30.94 33.58 36.22
2 42.91 48.19 53.47 58.75 64.03 69.31
3 62.80 70.72 78.64 86.56 94.48 102.40
4 82.69 93.25 103.81 114.37 124.93 135.49
5 102.58 115.78 128.98 142.18 155.38 168.58
6 122.47 138.31 154.15 169.99 185.83 201.67
7 142.36 160.84 179.32 197.80 216.28 234.76
8 162.25 183.37 204.49 225.61 246.73 267.85
9 182.14 205.90 229.66 253.42 277.18 300.94
10 202.03 228.43 254.83 281.23 307.63 334.03
11 221.92 250.96 280.00 309.04 338.08 367.12
12 241.81 273.49 305.17 336.85 368.53 400.21
13 261.70 296.02 330.34 364.66 398.98 433.30

编辑 1:附加背景和示例

我最初的问题似乎不清楚哪种优化条件普遍存在以及为什么。这个例子是可折叠/可伸缩太阳能电池板经济可行性计算工具链的一小部分。标题行 ( 1:1) 表示每个轨道的模块/面板数量,( A:A) 列表示轨道数量。因此,x 实际上表示每个轨道的模块或面板,y 表示轨道的数量。

在这个特定的工业案例模型中,对于钢支撑结构来说,每条轨道拥有更高密度的太阳能电池板总是比拥有更多轨道更经济。

当使用元组 (200,3) 作为(输入值,公差)时,三个结果元组符合选择标准:

(28,10)
(40,7)
(48,6)
Run Code Online (Sandbox Code Playgroud)

由于 48 个模块/轨道提供的密度高于 40 或 28 个模块,因此正确的最终解决方案是 x=48 和 y=6。

所提供的解决方案的当前情况如下(尽管我可能没有正确集成 Redy 的解决方案):


编辑 2:工作建议的解决方案

似乎至少有两个可行的解决方案:

Fle*_*ata 3

注意:此答案依赖于拥有最新版本的 Excel for Microsoft 365(已测试)或可能的 Excel for the Web(未测试)。

编辑以考虑多个匹配条件中的“最大列索引”:

调整方法以选择与最大列标题匹配的方法,这应该适用于单元格 J4:

=LET(
    data, A1:G14,
    row_headers, DROP(TAKE(data, , 1), 1),
    col_headers, DROP(TAKE(data, 1), , 1),
    d, DROP(data, 1, 1),
    v, I2,
    tol, J2,
    cols, COLUMNS(d),
    ascol, TOCOL(d),
    position, SEQUENCE(ROWS(ascol)),
    row, ROUNDUP(position / cols, 0),
    col, MOD(position + cols - 1, cols) + 1,
    arr, HSTACK(ascol, row, col),
    include, BYROW(ascol, LAMBDA(x, AND(x >= (v - tol), x <= (v + tol)))),
    matches, FILTER(arr, include),
    sorted, SORT(matches, {3, 2}, {-1, -1}),
    is_best, TAKE(sorted, 1),
    result, VSTACK(
        INDEX(col_headers, 1, INDEX(is_best, 1, 3)),
        INDEX(row_headers, INDEX(is_best, 1, 2), 1),
        INDEX(is_best, 1, 1)
    ),
    result
)
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

或者作为 LAMBDA:

get_best_match = LAMBDA(data, value, tolerance,
    LET(
        //get the headers
        row_headers, DROP(TAKE(data, , 1), 1),
        col_headers, DROP(TAKE(data, 1), , 1),

        //just the search array
        d, DROP(data, 1, 1),
        v, value,
        tol, tolerance,
        cols, COLUMNS(d),

        //convert the array to a column with row and column positions added
        ascol, TOCOL(d),
        position, SEQUENCE(ROWS(ascol)),
        row, ROUNDUP(position / cols, 0),
        col, MOD(position + cols - 1, cols) + 1,
        arr, HSTACK(ascol, row, col),

        //filter for those rows that meet the criteria
        include, BYROW(ascol, LAMBDA(x, AND(x >= (v - tol), x <= (v + tol)))),
        matches, FILTER(arr, include),

        //sort descending on column header, row header
        sorted, SORT(matches, {3, 2}, {-1, -1}),

        //take the first row of the sorted matches
        is_best, TAKE(sorted, 1),

        result, VSTACK(
            INDEX(col_headers, 1, INDEX(is_best, 1, 3)),
            INDEX(row_headers, INDEX(is_best, 1, 2), 1),
            INDEX(is_best, 1, 1)
        ),
        result
    )
);
Run Code Online (Sandbox Code Playgroud)

添加新标准之前的原始答案:

我相信这将在单元格 J4 中起作用:

=LET(
    data, A1:G14,
    row_headers, DROP(TAKE(data, , 1), 1),
    col_headers, DROP(TAKE(data, 1), , 1),
    d, DROP(data, 1, 1),
    v, I2,
    tol, J2,
    cols, COLUMNS(d),
    matches, MAP(d, LAMBDA(x, AND(x >= (v - tol), x <= (v + tol)))),
    is_best, d = MAX(d * matches),
    best_position, MAX(is_best * SEQUENCE(ROWS(d), cols)),
    y, ROUNDUP(best_position / cols, 0),
    x, MOD(best_position + cols - 1, cols) + 1,
    result, VSTACK(
        INDEX(col_headers, 1, x),
        INDEX(row_headers, y, 1),
        MAX(d * matches)
    ),
    result
)
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

我们可以在高级公式环境中对其进行注释并保存为命名的 LAMBDA,如下所示:

get_headers = LAMBDA(data,test,tolerance,
    LET(
        //get just the row index from column 1, rows 2:max
        row_headers, DROP(TAKE(data, , 1), 1),

        //get just the column headers from row 1, columns 2:max
        col_headers, DROP(TAKE(data, 1), , 1),

        //get just the array of data to test against
        d, DROP(data, 1, 1),

        //get the number of columns in the array (the )
        cols, COLUMNS(d),

        //test each value against the boundaries
        matches, MAP(d, LAMBDA(x, AND(x >= (test - tolerance), x <= (test + tolerance)))),

        //identify the best value
        is_best, d = MAX(d * matches),

        //find the position of the best value
        best_position, MAX(is_best * SEQUENCE(ROWS(d), cols)),

        //identify the row index
        y, ROUNDUP(best_position / cols, 0),

        //identify the column index
        x, MOD(best_position + cols - 1, cols) + 1,

        //return the column header, the row header and the best value
        result, VSTACK(
            INDEX(col_headers, 1, x),
            INDEX(row_headers, y, 1),
            MAX(d * matches)
        ),
        result
    )
)
Run Code Online (Sandbox Code Playgroud)