Excel,根据布尔值计算列表中下一个值的条件函数
Excel, conditional function to calculate next value within a list based on boolean
抱歉,如果标题不够清楚。我没有找到一种很好的方式来表达我在这里想要实现的目标。我是 excel 新手,所以不知道从哪里开始寻找我正在寻找的逻辑。
我有一个电子表格,我想用它来安排学生走廊的清洁工作。问题是房间并不总是有人住,所以它需要能够反应下一个房间是否有人,如果没有,select 列表中的下一个。
在这张图片中我们可以看到第 22 周是如何在 1902 年和 1903 年打扫的,下周轮到 [1901,1902,1903,1904,1905,1906,1907,1908,1909,1910] 中的下两个房间,1911],只要他们被预订(右侧列中的值 1)。 1904 和 1905 已预订(绿色)所以下周将轮到他们。
如果我们继续相同的逻辑,下一轮将是 1906 和 1907,下周(第 25 周)应该是 1908 和 1909,问题是 1908 房间现在是空的,所以它不应该被算作符合条件的房间,而是选择1909&1910作为下一个打扫房间
如何使用 excel 电子表格实现此逻辑?
这是我的电子表格副本的 link,因此您可以看到我正在努力实现的目标,并在需要时使用原始数据。
方法
- 确定上次清洁的房间
- 创建下一个已占用房间的列表
- 确定接下来要清洁的两个已占用房间
- 预订少于两个房间的处理情况
第 1 步:确定上次清洁的房间
我创建了一个名为“设置”的新选项卡,用户可以在其中从下拉列表中select 最后一个已打扫过的房间。单元格 $C$3 可通过范围名称 setup_lastcleaned_room.
访问
主选项卡上的单元格 $F$2 引用此 =setup_lastcleaned_room
。
步骤 02:创建下一个已占用房间的列表
第U栏(黄色区域)指的是最后打扫过的房间,由上一行F栏确定。我们在步骤 04 中研究这个公式。
列 V(橙色区域)确定“最后清理的房间”在房间列表 =MATCH(U3,list_rooms,0)
和 [=143 中的位置=] 是“=Schedule!$I$1:$S$1”的范围名称(选项卡的名称是“Schedule”)。
列 W 到 AG(浅绿色区域)利用 OFFSET-公式列出接下来必须清洁的房间,如果他们被占用。
=IF($V3+W<=11,IF(OFFSET($H,ROW()-1,$V3+W)=1,OFFSET($H,0,$V3+W),""),IF(OFFSET($H,ROW()-1,$V3+W-11)=1,OFFSET($H,0,$V3+W-11),""))
.
看起来很复杂?让我们把它分成几部分:
=IF(last_room_on_list_not_reached, handle_end_of_list, handle_beginning_of_list)
.
last_room_on_list_not_reached = $V3+W<=11
:我们检查“最后打扫过的房间”加上“第x个房间”会超出我们的房间列表。
handle_end_of_list=IF(OFFSET($H,ROW()-1,$V3+W)=1,OFFSET($H,0,$V3+W),"")
分别为:IF(is_room_booked,show_room_name,"")
is_room_booked = OFFSET($H,ROW()-1,$V3+W)=1
:我们移动anchor单元格($H $1) 由当前行数减一(锚点的行号)和由“最后一个房间清洁的位置”+“第 x 个房间”确定的列数。接下来,我们检查此房间是否已预订(单元格内容 = 1)。
show_room_name = OFFSET($H,0,$V3+W)
:这次我们将 anchor 单元格移动仅用于获取房间名称的列数(见上文)(与我们的 anchor 在同一行)。
handle_beginning_of_list:类似于“handle_end_of_list”,但这次列的确定略有不同。
列 AH(深绿色区域)将所有房间名称连接在一起,以实现以下房间名称的字符串:=W3&X3&Y3&Z3&AA3&AB3&AC3&AD3&AE3&AF3&AG3&"NONENONE"
。我们还在字符串中添加了两个占位符 ("NONE") 以获得结果,即使预订的房间少于两个也是如此。
请注意此解决方案考虑到所有房间名称(和占位符)都由固定数量的字符(四个)组成。如果不是这种情况,我们将不得不包括一个分隔符,并且下面的公式会稍微复杂一些。
第 3 步:确定接下来要清洁的两个已占用房间。
所有的四字房间名都按要求的顺序排列后,就很容易确定接下来需要打扫的两个房间了。
列E(浅蓝色区域)和F(深蓝色区域)检索第一个resp。本周应该打扫的第二个房间:=MID($AH3,1,4)
resp。 =MID($AH3,5,4)
.
列 D(紫色区域)用破折号连接两个房间以重新创建提问者的样本:=E3&"-"&F3
.
第 4 步:预订少于两个房间的情况处理
每周,我们都会确定最后一个打扫过的房间。但是,如果 none 或仅预订了一个怎么办?我们无法确定有效的房间,因此不知道要继续使用哪个房间。因此,我们检查上面一排已经打扫过的第二个房间,如果它是“none”,我们寻找第一个房间。如果它也是“none”,我们将使用在 setup_lastcleaned_room 中定义的房间:
=IF(F2="NONE",IF(E2="NONE",setup_lastcleaned_room,VALUE(E2)),VALUE(F2))
。 注意事项:我们使用“VALUE”,因为房间名称是数字,但在连接时变成了字符串。
简答
我很确定是这样的:
=IF(NOT(ISNA(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-1)), INDEX($G:$Q,1,MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-1) & "-" & IF(NOT(ISNA(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-1,COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-1,COLUMNS($G:$Q)))), INDEX($G:$Q,1,MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-1,COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-1,COLUMNS($G:$Q))), INDEX($G:$Q,1,MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q))):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q) + MOD(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-1,COLUMNS($G:$Q))+1-2)),0))), INDEX($G:$Q,1,MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q))):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q) + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-2)),0)) & "-" & IF(NOT(ISNA(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q))):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q) + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-2)),0),COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q))):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q) + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-2)),0),COLUMNS($G:$Q)))), INDEX($G:$Q,1,MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q))):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q) + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-2)),0),COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q))):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q) + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-2)),0),COLUMNS($G:$Q))), INDEX($G:$Q,1,MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q))):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q) + MOD(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q))):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q) + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-2)),0),COLUMNS($G:$Q))+1-2)),0))))
将其粘贴到已经有转弯的行下方,例如D6
(第 27 周轮到)。此公式使用上一回合和房间名称范围 ($G:$Q
) 来确定下一回合应该是什么。
长答案
等等,这是什么怪物!?这也是我见过的最令人生畏的公式!
好吧,里面有很多重复,所以它是独立工作的。它还做了很多繁重的工作,例如灵活地考虑可能有多少个房间,当前行是什么以及任何正在预订的房间。可能有更多的逻辑来处理边缘情况,例如,如果所有房间都被预订了怎么办? (目前是returns#N/A
),但这解决了根本问题。
将其分解为以下步骤:
查找最后打扫过的房间
以单元格D6
为起点,最后一个要打扫的房间是1910。所以,我们要在单元格D5
中找到这个值:
=INT(RIGHT(D5, LEN(D5)-FIND("-", D5)))
这只是从 D5
中获取文本并提取最后一个数字。
在
范围内定位
范围 $G:$Q
是第一行指定房间号的单元格列表($
表示这是固定的,因此如果将此公式向下拖动,例如,范围仍将引用 G1:Q1 而不是它下面的任何内容)。
此范围的第 1 列 (G
) 是电子表格中的第 7 列。这个(7)可以用下面的公式求得:
=COLUMN($G:$Q)
范围 (11) 中的列数是:
=COLUMNS($G:$Q)
要找到范围内最后打扫过的房间,我们使用 MATCH 和 Match_type = 0:
MATCH finds the first value that is exactly equal to lookup_value.
=MATCH([最后的房间号],$G$1:$Q$1,0)
=MATCH( INT(RIGHT(D5, LEN(D5)-FIND("-", D5))) ,$G:$Q,0)
此 returns 匹配单元格的相对列号,从 1 到 11(1910 在第 10 列)。
确定目标范围
我们现在要开始查看之后的下一专栏。我们可以简单地加 1,这很好用,除非最后一个要打扫的房间是 1911。在那种情况下,我们不能只加 1,因为没有第 12 个房间。在那里,我们想回到第一个房间。为此,我们首先使用 MOD 并除以列数:
=MOD([相对列号],$G$1:$Q$1,0),[列数])+1
=MOD( MATCH( INT(RIGHT(D5, LEN(D5)-FIND("-", D5))) ,$G:$Q,0), COLUMNS($G:$Q) ) +1
由此返回的数字是开始的列。所以,如果最后一个房间是 1910,那么结果就是 11。结束的列是最后一列,11(列数)。
但是,如果在这个范围内没有找到未预订的房间,那么我们就得从第一个到开始前的那个再绕一遍。这意味着我们总是需要做两次检查来维护房间的顺序。
在我们的示例中:
Check
Start
End
Range
1
11
11
Q6:Q6
2
1
10
G6:P6
作为伪公式:
Check
Start
End
Range
1
MOD( [Relative column number] ,$G:$Q,0), [Column count] ) +1
[Column count]
=INDIRECT(ADDRESS(ROW(), [Column count before first] + [Start] )):INDIRECT(ADDRESS(ROW(), [Column count before first] + [End] ))
2
1
[Last room column]
=INDIRECT(ADDRESS(ROW(), [Column count before first] + [Start] )):INDIRECT(ADDRESS(ROW(), [Column count before first] + [End] ))
如公式:
Check
Start
End
Range
1
=MOD(MATCH( INT(RIGHT(D5, LEN(D5)-FIND("-", D5))) ,$G:$Q,0), COLUMNS($G:$Q) ) +1
=COLUMNS($G:$Q)
=INDIRECT(ADDRESS(ROW(), COLUMN($G:$Q)-1 + MOD(MATCH( INT(RIGHT(D5, LEN(D5)-FIND("-", D5))) ,$G:$Q,0), COLUMNS($G:$Q) ) +1 )):INDIRECT(ADDRESS(ROW(), COLUMN($G:$Q)-1 + COLUMNS($G:$Q) ))
2
1
=MOD(MATCH( INT(RIGHT(D5, LEN(D5)-FIND("-", D5))) ,$G:$Q,0), COLUMNS($G:$Q) )
=INDIRECT(ADDRESS(ROW(), COLUMN($G:$Q) )):INDIRECT(ADDRESS(ROW(), COLUMN($G:$Q)-1 + MOD(MATCH( INT(RIGHT(D5, LEN(D5)-FIND("-", D5))) ,$G:$Q,0), COLUMNS($G:$Q) ) ))
需要做一些公式杂技来将其描述为 Excel 的范围。这些范围指定当前行,并给出绝对列引用(请记住,范围的第 1 列是电子表格的第 7 列,因此我们需要添加 6,或 =COLUMN($G:$Q) -1
到这些)
查找下一个未预订的房间
现在我们可以将其与 MATCH
结合使用,在每种情况下查找下一个未预订的房间:
=MATCH(1, [范围] ,0) + [开始] -1
=MATCH(1, INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH( INT(RIGHT(D5, LEN(D5)-FIND("-", D5))) ,$G:$Q,0), COLUMNS($G:$Q) ) +1 )):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q) )) ,0)+ MOD(MATCH( INT(RIGHT(D5, LEN(D5)-FIND("-", D5))) ,$G:$Q,0), COLUMNS($G:$Q) )
(Returns11)
=匹配(1, [范围] ,0)
=MATCH(1, INDIRECT(ADDRESS(ROW(), COLUMN($G:$Q) )):INDIRECT(ADDRESS(ROW(), COLUMN($G:$Q)-1 + MOD(MATCH( INT(RIGHT(D5, LEN(D5)-FIND("-", D5))) ,$G:$Q,0), COLUMNS($G:$Q) ) )) ,0)
(这稍微简单一些,因为开始总是 1)
(Returns 1)
这意味着在第 11 和第 1 列中找到了 1
的值。
查找房间号
要从这些列号中找到实际房间号,我们使用 INDEX:
=INDEX($G$1:$Q$1,1, [匹配列号])
Returns 以上为 1901 和 1911。
条件逻辑
如果第一次检查找到一个数字(否则返回 #N/A
),我们不需要第二次。换句话说,如果在上一个房间之后找到了下一个未预订的房间,那么我们就不需要绕到1901房间继续检查了。在 Excel 中,这是一个 IF 函数:
=IF(NOT(ISNA( [Check 1] )), INDEX($G$1:$Q$1,1, [Check 1]), INDEX($G$1:$Q $1,1, [检查 2]))
但是,我们正在搜索两个房间,可能在第一次和第二次检查中,或者都在第一次检查中,或者都在第二次检查中,或者没有找到。由于 MATCH
只找到范围内的第一个匹配,我们需要做一次,根据结果调整范围,然后再做一遍。整个公式变成了IF
个函数的组合,像这样:
=IF(NOT(ISNA( [检查 1] )),
INDEX($G$1:$Q$1,1, [检查 1]) & "-" & IF(NOT(ISNA( [检查 3] )), INDEX($G$1 :$Q$1,1, [检查 3]), INDEX($G$1:$Q$1,1, [检查 4])),
INDEX($G$1:$Q$1,1, [Check 2]) & "-" & IF(NOT(ISNA( [Check 3] )), INDEX($G$1 :$Q$1,1, [检查 3]), INDEX($G$1:$Q$1,1, [检查 4]))
)
将所有这些结合起来,结果就是从一开始就形成的大公式。
备选答案
这实际上在 VBA 代码中看起来简单得多。 Here 是关于如何在 Excel 中进行设置的指南。使用以下功能:
Public Function choose_turns(last As Range, rooms As Range) As String
Dim row As Integer
Dim rooms_first_column As Integer
Dim rooms_count As Integer
Dim last_room_number As Integer
Dim last_room_column As Integer
Dim count_rooms_found As Integer
Dim start_column As Integer
Dim end_column As Integer
Dim found_column As Integer
Dim target As Range
Dim result As String
'Find which room was cleaned last
last_room_number = Int(Mid(last.Value, InStr(last.Value, "-") + 1))
'Locate that within the range
rooms_first_column = rooms.Column
rooms_count = rooms.Columns.Count
last_room_column = Application.Match(last_room_number, rooms, 0)
'Conditional logic
row = last.row + 1
count_rooms_found = 0
start_column = last_room_column
Do While count_rooms_found < 2
found_column = 0
'Check 1
'Identify target range
start_column = (start_column Mod rooms_count) + 1
end_column = rooms_count
Set target = Range(Cells(row, rooms_first_column - 1 + start_column), Cells(row, rooms_first_column - 1 + end_column))
'Find next unbooked room
If Not IsError(Application.Match(1, target, 0)) Then
found_column = Application.Match(1, target, 0) + start_column - 1
Else
'Check 2
'Identify target range
start_column = 1
end_column = last_room_column
Set target = Range(Cells(row, rooms_first_column - 1 + start_column), Cells(row, rooms_first_column - 1 + end_column))
'Find next unbooked room
If Not IsError(Application.Match(1, target, 0)) Then
found_column = Application.Match(1, target, 0) + start_column - 1
End If
End If
If found_column = 0 Then
Exit Do
End If
If 0 < Len(result) Then
result = result + "-"
End If
'Finding room numbers
result = result + CStr(Application.WorksheetFunction.Index(rooms, 1, found_column))
count_rooms_found = count_rooms_found + 1
Loop
choose_turns = result
End Function
然后,在您的工作表中,您可以将其用作这样的公式(例如在 D6
中):
=choose_turns(D5, $G:$Q)
如果您有支持动态数组的 Excel 版本,请考虑(在 D2
中并向下复制)
=LET(Rooms,$G:$Q,
Use,$G2:$Q2,
Used,FILTER(Rooms,Use=1),
Avail,COUNT(Used),
Cols,SEQUENCE(1,Avail*2, 1, 1),
Roster,INDEX(Used, 1, IF(Cols<=Avail, Cols, Cols-Avail)),
Prev,IF(D1="Turn", INDEX(Used, 1, Avail), --MID(D1,6,4)),
First,INDEX(Roster, 1, XMATCH(Prev, Roster, -1)+1),
Second,INDEX(Roster, 1, XMATCH(First, Roster, 0)+1),
First&"-"&Second)
说明
Rooms
- 范围参考房间名称
Occupancy
- 相关周的房间入住率范围
Used
- 已过滤的本周已入住房间列表
Avail
- 已入住房间数
Cols
- 序列 1
到 Avail * 2. Used to index the
Used` 列表
Roster
- 使用过的房间的数组,重复(所以我们不必担心包装)
Prev
- 上一行结果中的第二个房间
First
- 第一个结果。从 Prev
房间 进入花名册的索引
Second
- 第二个结果。从 First
房间 进入花名册的索引
注意:如果任何一周只有 0
或 1
个房间被占用,这将 return 错误。这是否有可能,如果有,您的预期结果是什么?
演示
抱歉,如果标题不够清楚。我没有找到一种很好的方式来表达我在这里想要实现的目标。我是 excel 新手,所以不知道从哪里开始寻找我正在寻找的逻辑。
我有一个电子表格,我想用它来安排学生走廊的清洁工作。问题是房间并不总是有人住,所以它需要能够反应下一个房间是否有人,如果没有,select 列表中的下一个。
在这张图片中我们可以看到第 22 周是如何在 1902 年和 1903 年打扫的,下周轮到 [1901,1902,1903,1904,1905,1906,1907,1908,1909,1910] 中的下两个房间,1911],只要他们被预订(右侧列中的值 1)。 1904 和 1905 已预订(绿色)所以下周将轮到他们。
如果我们继续相同的逻辑,下一轮将是 1906 和 1907,下周(第 25 周)应该是 1908 和 1909,问题是 1908 房间现在是空的,所以它不应该被算作符合条件的房间,而是选择1909&1910作为下一个打扫房间
如何使用 excel 电子表格实现此逻辑?
这是我的电子表格副本的 link,因此您可以看到我正在努力实现的目标,并在需要时使用原始数据。
方法
- 确定上次清洁的房间
- 创建下一个已占用房间的列表
- 确定接下来要清洁的两个已占用房间
- 预订少于两个房间的处理情况
第 1 步:确定上次清洁的房间
我创建了一个名为“设置”的新选项卡,用户可以在其中从下拉列表中select 最后一个已打扫过的房间。单元格 $C$3 可通过范围名称 setup_lastcleaned_room.
访问主选项卡上的单元格 $F$2 引用此 =setup_lastcleaned_room
。
步骤 02:创建下一个已占用房间的列表
第U栏(黄色区域)指的是最后打扫过的房间,由上一行F栏确定。我们在步骤 04 中研究这个公式。
列 V(橙色区域)确定“最后清理的房间”在房间列表 =MATCH(U3,list_rooms,0)
和 [=143 中的位置=] 是“=Schedule!$I$1:$S$1”的范围名称(选项卡的名称是“Schedule”)。
列 W 到 AG(浅绿色区域)利用 OFFSET-公式列出接下来必须清洁的房间,如果他们被占用。
=IF($V3+W<=11,IF(OFFSET($H,ROW()-1,$V3+W)=1,OFFSET($H,0,$V3+W),""),IF(OFFSET($H,ROW()-1,$V3+W-11)=1,OFFSET($H,0,$V3+W-11),""))
.
看起来很复杂?让我们把它分成几部分:
=IF(last_room_on_list_not_reached, handle_end_of_list, handle_beginning_of_list)
.
last_room_on_list_not_reached =
$V3+W<=11
:我们检查“最后打扫过的房间”加上“第x个房间”会超出我们的房间列表。handle_end_of_list=
IF(OFFSET($H,ROW()-1,$V3+W)=1,OFFSET($H,0,$V3+W),"")
分别为:IF(is_room_booked,show_room_name,"")
is_room_booked =
OFFSET($H,ROW()-1,$V3+W)=1
:我们移动anchor单元格($H $1) 由当前行数减一(锚点的行号)和由“最后一个房间清洁的位置”+“第 x 个房间”确定的列数。接下来,我们检查此房间是否已预订(单元格内容 = 1)。show_room_name =
OFFSET($H,0,$V3+W)
:这次我们将 anchor 单元格移动仅用于获取房间名称的列数(见上文)(与我们的 anchor 在同一行)。handle_beginning_of_list:类似于“handle_end_of_list”,但这次列的确定略有不同。
列 AH(深绿色区域)将所有房间名称连接在一起,以实现以下房间名称的字符串:=W3&X3&Y3&Z3&AA3&AB3&AC3&AD3&AE3&AF3&AG3&"NONENONE"
。我们还在字符串中添加了两个占位符 ("NONE") 以获得结果,即使预订的房间少于两个也是如此。
请注意此解决方案考虑到所有房间名称(和占位符)都由固定数量的字符(四个)组成。如果不是这种情况,我们将不得不包括一个分隔符,并且下面的公式会稍微复杂一些。
第 3 步:确定接下来要清洁的两个已占用房间。
所有的四字房间名都按要求的顺序排列后,就很容易确定接下来需要打扫的两个房间了。
列E(浅蓝色区域)和F(深蓝色区域)检索第一个resp。本周应该打扫的第二个房间:=MID($AH3,1,4)
resp。 =MID($AH3,5,4)
.
列 D(紫色区域)用破折号连接两个房间以重新创建提问者的样本:=E3&"-"&F3
.
第 4 步:预订少于两个房间的情况处理
每周,我们都会确定最后一个打扫过的房间。但是,如果 none 或仅预订了一个怎么办?我们无法确定有效的房间,因此不知道要继续使用哪个房间。因此,我们检查上面一排已经打扫过的第二个房间,如果它是“none”,我们寻找第一个房间。如果它也是“none”,我们将使用在 setup_lastcleaned_room 中定义的房间:
=IF(F2="NONE",IF(E2="NONE",setup_lastcleaned_room,VALUE(E2)),VALUE(F2))
。 注意事项:我们使用“VALUE”,因为房间名称是数字,但在连接时变成了字符串。
简答
我很确定是这样的:
=IF(NOT(ISNA(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-1)), INDEX($G:$Q,1,MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-1) & "-" & IF(NOT(ISNA(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-1,COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-1,COLUMNS($G:$Q)))), INDEX($G:$Q,1,MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-1,COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-1,COLUMNS($G:$Q))), INDEX($G:$Q,1,MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q))):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q) + MOD(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-1,COLUMNS($G:$Q))+1-2)),0))), INDEX($G:$Q,1,MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q))):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q) + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-2)),0)) & "-" & IF(NOT(ISNA(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q))):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q) + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-2)),0),COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q))):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q) + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-2)),0),COLUMNS($G:$Q)))), INDEX($G:$Q,1,MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q))):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q) + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-2)),0),COLUMNS($G:$Q))+1)):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q))),0)+MOD(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q))):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q) + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-2)),0),COLUMNS($G:$Q))), INDEX($G:$Q,1,MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q))):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q) + MOD(MATCH(1,INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q))):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q) + MOD(MATCH(INT(RIGHT(D5, LEN(D5)-FIND("-", D5))),$G:$Q,0),COLUMNS($G:$Q))+1-2)),0),COLUMNS($G:$Q))+1-2)),0))))
将其粘贴到已经有转弯的行下方,例如D6
(第 27 周轮到)。此公式使用上一回合和房间名称范围 ($G:$Q
) 来确定下一回合应该是什么。
长答案
等等,这是什么怪物!?这也是我见过的最令人生畏的公式!
好吧,里面有很多重复,所以它是独立工作的。它还做了很多繁重的工作,例如灵活地考虑可能有多少个房间,当前行是什么以及任何正在预订的房间。可能有更多的逻辑来处理边缘情况,例如,如果所有房间都被预订了怎么办? (目前是returns#N/A
),但这解决了根本问题。
将其分解为以下步骤:
查找最后打扫过的房间
以单元格D6
为起点,最后一个要打扫的房间是1910。所以,我们要在单元格D5
中找到这个值:
=INT(RIGHT(D5, LEN(D5)-FIND("-", D5)))
这只是从 D5
中获取文本并提取最后一个数字。
在
范围内定位范围 $G:$Q
是第一行指定房间号的单元格列表($
表示这是固定的,因此如果将此公式向下拖动,例如,范围仍将引用 G1:Q1 而不是它下面的任何内容)。
此范围的第 1 列 (G
) 是电子表格中的第 7 列。这个(7)可以用下面的公式求得:
=COLUMN($G:$Q)
范围 (11) 中的列数是:
=COLUMNS($G:$Q)
要找到范围内最后打扫过的房间,我们使用 MATCH 和 Match_type = 0:
MATCH finds the first value that is exactly equal to lookup_value.
=MATCH([最后的房间号],$G$1:$Q$1,0)
=MATCH( INT(RIGHT(D5, LEN(D5)-FIND("-", D5))) ,$G:$Q,0)
此 returns 匹配单元格的相对列号,从 1 到 11(1910 在第 10 列)。
确定目标范围
我们现在要开始查看之后的下一专栏。我们可以简单地加 1,这很好用,除非最后一个要打扫的房间是 1911。在那种情况下,我们不能只加 1,因为没有第 12 个房间。在那里,我们想回到第一个房间。为此,我们首先使用 MOD 并除以列数:
=MOD([相对列号],$G$1:$Q$1,0),[列数])+1
=MOD( MATCH( INT(RIGHT(D5, LEN(D5)-FIND("-", D5))) ,$G:$Q,0), COLUMNS($G:$Q) ) +1
由此返回的数字是开始的列。所以,如果最后一个房间是 1910,那么结果就是 11。结束的列是最后一列,11(列数)。
但是,如果在这个范围内没有找到未预订的房间,那么我们就得从第一个到开始前的那个再绕一遍。这意味着我们总是需要做两次检查来维护房间的顺序。
在我们的示例中:
Check | Start | End | Range |
---|---|---|---|
1 | 11 | 11 | Q6:Q6 |
2 | 1 | 10 | G6:P6 |
作为伪公式:
Check | Start | End | Range |
---|---|---|---|
1 | MOD( [Relative column number] ,$G:$Q,0), [Column count] ) +1 | [Column count] | =INDIRECT(ADDRESS(ROW(), [Column count before first] + [Start] )):INDIRECT(ADDRESS(ROW(), [Column count before first] + [End] )) |
2 | 1 | [Last room column] | =INDIRECT(ADDRESS(ROW(), [Column count before first] + [Start] )):INDIRECT(ADDRESS(ROW(), [Column count before first] + [End] )) |
如公式:
Check | Start | End | Range |
---|---|---|---|
1 | =MOD(MATCH( INT(RIGHT(D5, LEN(D5)-FIND("-", D5))) ,$G:$Q,0), COLUMNS($G:$Q) ) +1 |
=COLUMNS($G:$Q) |
=INDIRECT(ADDRESS(ROW(), COLUMN($G:$Q)-1 + MOD(MATCH( INT(RIGHT(D5, LEN(D5)-FIND("-", D5))) ,$G:$Q,0), COLUMNS($G:$Q) ) +1 )):INDIRECT(ADDRESS(ROW(), COLUMN($G:$Q)-1 + COLUMNS($G:$Q) )) |
2 | 1 |
=MOD(MATCH( INT(RIGHT(D5, LEN(D5)-FIND("-", D5))) ,$G:$Q,0), COLUMNS($G:$Q) ) |
=INDIRECT(ADDRESS(ROW(), COLUMN($G:$Q) )):INDIRECT(ADDRESS(ROW(), COLUMN($G:$Q)-1 + MOD(MATCH( INT(RIGHT(D5, LEN(D5)-FIND("-", D5))) ,$G:$Q,0), COLUMNS($G:$Q) ) )) |
需要做一些公式杂技来将其描述为 Excel 的范围。这些范围指定当前行,并给出绝对列引用(请记住,范围的第 1 列是电子表格的第 7 列,因此我们需要添加 6,或 =COLUMN($G:$Q) -1
到这些)
查找下一个未预订的房间
现在我们可以将其与 MATCH
结合使用,在每种情况下查找下一个未预订的房间:
=MATCH(1, [范围] ,0) + [开始] -1
=MATCH(1, INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + MOD(MATCH( INT(RIGHT(D5, LEN(D5)-FIND("-", D5))) ,$G:$Q,0), COLUMNS($G:$Q) ) +1 )):INDIRECT(ADDRESS(ROW(),COLUMN($G:$Q)-1 + COLUMNS($G:$Q) )) ,0)+ MOD(MATCH( INT(RIGHT(D5, LEN(D5)-FIND("-", D5))) ,$G:$Q,0), COLUMNS($G:$Q) )
(Returns11)
=匹配(1, [范围] ,0)
=MATCH(1, INDIRECT(ADDRESS(ROW(), COLUMN($G:$Q) )):INDIRECT(ADDRESS(ROW(), COLUMN($G:$Q)-1 + MOD(MATCH( INT(RIGHT(D5, LEN(D5)-FIND("-", D5))) ,$G:$Q,0), COLUMNS($G:$Q) ) )) ,0)
(这稍微简单一些,因为开始总是 1)
(Returns 1)
这意味着在第 11 和第 1 列中找到了 1
的值。
查找房间号
要从这些列号中找到实际房间号,我们使用 INDEX:
=INDEX($G$1:$Q$1,1, [匹配列号]) Returns 以上为 1901 和 1911。
条件逻辑
如果第一次检查找到一个数字(否则返回 #N/A
),我们不需要第二次。换句话说,如果在上一个房间之后找到了下一个未预订的房间,那么我们就不需要绕到1901房间继续检查了。在 Excel 中,这是一个 IF 函数:
=IF(NOT(ISNA( [Check 1] )), INDEX($G$1:$Q$1,1, [Check 1]), INDEX($G$1:$Q $1,1, [检查 2]))
但是,我们正在搜索两个房间,可能在第一次和第二次检查中,或者都在第一次检查中,或者都在第二次检查中,或者没有找到。由于 MATCH
只找到范围内的第一个匹配,我们需要做一次,根据结果调整范围,然后再做一遍。整个公式变成了IF
个函数的组合,像这样:
=IF(NOT(ISNA( [检查 1] )),
INDEX($G$1:$Q$1,1, [检查 1]) & "-" & IF(NOT(ISNA( [检查 3] )), INDEX($G$1 :$Q$1,1, [检查 3]), INDEX($G$1:$Q$1,1, [检查 4])),
INDEX($G$1:$Q$1,1, [Check 2]) & "-" & IF(NOT(ISNA( [Check 3] )), INDEX($G$1 :$Q$1,1, [检查 3]), INDEX($G$1:$Q$1,1, [检查 4]))
)
将所有这些结合起来,结果就是从一开始就形成的大公式。
备选答案
这实际上在 VBA 代码中看起来简单得多。 Here 是关于如何在 Excel 中进行设置的指南。使用以下功能:
Public Function choose_turns(last As Range, rooms As Range) As String
Dim row As Integer
Dim rooms_first_column As Integer
Dim rooms_count As Integer
Dim last_room_number As Integer
Dim last_room_column As Integer
Dim count_rooms_found As Integer
Dim start_column As Integer
Dim end_column As Integer
Dim found_column As Integer
Dim target As Range
Dim result As String
'Find which room was cleaned last
last_room_number = Int(Mid(last.Value, InStr(last.Value, "-") + 1))
'Locate that within the range
rooms_first_column = rooms.Column
rooms_count = rooms.Columns.Count
last_room_column = Application.Match(last_room_number, rooms, 0)
'Conditional logic
row = last.row + 1
count_rooms_found = 0
start_column = last_room_column
Do While count_rooms_found < 2
found_column = 0
'Check 1
'Identify target range
start_column = (start_column Mod rooms_count) + 1
end_column = rooms_count
Set target = Range(Cells(row, rooms_first_column - 1 + start_column), Cells(row, rooms_first_column - 1 + end_column))
'Find next unbooked room
If Not IsError(Application.Match(1, target, 0)) Then
found_column = Application.Match(1, target, 0) + start_column - 1
Else
'Check 2
'Identify target range
start_column = 1
end_column = last_room_column
Set target = Range(Cells(row, rooms_first_column - 1 + start_column), Cells(row, rooms_first_column - 1 + end_column))
'Find next unbooked room
If Not IsError(Application.Match(1, target, 0)) Then
found_column = Application.Match(1, target, 0) + start_column - 1
End If
End If
If found_column = 0 Then
Exit Do
End If
If 0 < Len(result) Then
result = result + "-"
End If
'Finding room numbers
result = result + CStr(Application.WorksheetFunction.Index(rooms, 1, found_column))
count_rooms_found = count_rooms_found + 1
Loop
choose_turns = result
End Function
然后,在您的工作表中,您可以将其用作这样的公式(例如在 D6
中):
=choose_turns(D5, $G:$Q)
如果您有支持动态数组的 Excel 版本,请考虑(在 D2
中并向下复制)
=LET(Rooms,$G:$Q,
Use,$G2:$Q2,
Used,FILTER(Rooms,Use=1),
Avail,COUNT(Used),
Cols,SEQUENCE(1,Avail*2, 1, 1),
Roster,INDEX(Used, 1, IF(Cols<=Avail, Cols, Cols-Avail)),
Prev,IF(D1="Turn", INDEX(Used, 1, Avail), --MID(D1,6,4)),
First,INDEX(Roster, 1, XMATCH(Prev, Roster, -1)+1),
Second,INDEX(Roster, 1, XMATCH(First, Roster, 0)+1),
First&"-"&Second)
说明
Rooms
- 范围参考房间名称Occupancy
- 相关周的房间入住率范围Used
- 已过滤的本周已入住房间列表Avail
- 已入住房间数Cols
- 序列1
到Avail * 2. Used to index the
Used` 列表Roster
- 使用过的房间的数组,重复(所以我们不必担心包装)Prev
- 上一行结果中的第二个房间First
- 第一个结果。从Prev
房间 进入花名册的索引
Second
- 第二个结果。从First
房间 进入花名册的索引
注意:如果任何一周只有 0
或 1
个房间被占用,这将 return 错误。这是否有可能,如果有,您的预期结果是什么?
演示