查找电梯楼层运行时间表的排序算法,从当前楼层开始,预选到达的楼层和运行方向
Find sorting algorithm for an elevators floor travel schedule, starting from the current floor with preselected floors to reach and a travel direction
我正在用 React 制作电梯,但我需要制作一个函数,将数组排序到最接近数字 X 的位置,并且还有电梯上升或下降的条件,
例如,
- 共有5层。
- X = 当前楼层 3
您现在在 3 楼,点击按钮 UP 然后点击数字
2 -> 5 -> 4 -> 1
数组应该按如下方式排序:3 -> 4 -> 5 -> 2 -> 1 .
伪代码:
let currentFloor = 3;
let direction = "UP";
let clickedButtons = [2,5,4,1];
// ClickedButtons after sorted
clickedButtons = [4,5,2,1]
您可以将数组分成两个数组。一个仅包含比 currentFloor 更高的数字,另一个仅包含更低的数字,例如:
let lowerFloors = clickedButtons.filter(floor => floor > currnentFloor);
let upperFloors = clickedButtons.filter(floor => floor > currnentFloor);
然后对每个数组进行排序,lowerFloors 为降序,higherFloors 为升序。
upperFloors = upperFloors.sort((a, b) => {
return a - b;
});
lowerFloors = lowerFloors.sort((a, b) => {
return a - b;
});
lowerFloors = lowerFloors.reverse();
然后合并两个数组。如果你上升 upperFloors 数组将是第一个,否则 lowerFloors 将是第一个。
let newArray;
if (direction === "UP") {
newArray = upperFloors.concat(lowerFloors);
} else {
newArray = lowerFloors.concat(upperFloors)
}
我猜你需要这样的东西
无论何时上楼,您都需要找到所有更大的数字并将它们按升序排序,以便电梯在上升时停在下一个可能的楼层。
然后找出所有较小的数字并倒序排列。
如果选择向下,将执行相反的过程。
let arr = [10, 4, 8, 1, -2, 6];
let x = 3;
const goUp = (num, arr) => arr.filter(obj => obj > num).sort((a, b) => a - b)
const goDown = (num, arr) => arr.filter(obj => obj < num).sort((a, b) => b - a)
function printSequence(arr, initial, direction) {
if (direction === 'up') {
console.log(goUp(initial, arr).concat(goDown(initial, arr)))
} else {
console.log(goDown(initial, arr).concat(goUp(initial, arr)))
}
}
printSequence(arr, 3, 'up')
printSequence(arr, 3, 'down')
根据我上面的两条评论...
@Cedric ... 1/2 With this scenario the only sorting needed is e.g. an ascending sorting of all floor numbers ... [1, 2, 3, 4, 5]
... one find's the index of the current floor number 3
which is 2
... 'up'
will be translated into direction vector of 1
, thus one takes 1st everything higher than index 2
or right from index 2
... and 2nd everything left from index 2
... result ... [4, 5, 2, 1]
.
@Cedric ... 2/2 One does likewise for 'down'
/-1
where one 1st takes everything left from index 2
and 2nd everything right from index 2
... result ... [2, 1, 4, 5]
. There is no sorting magic since with the known preset of either 'up'
or 'down'
the floor travel schedule is obvious.
...但是人们当然可以将我的评论的命令式配方转化为单一排序公式。
function getFloorTravelSchedule(
direction = 'up',
floorNumber = 0,
floorTravelList = [],
) {
direction = ((direction === 'up') && 1) || -1;
function getTravelPrecedence(a, b) {
return (
(a > floorNumber && b > floorNumber && a - b) ||
(a < floorNumber && b < floorNumber && b - a) ||
((a > floorNumber && b < floorNumber && -1) || 1) * direction
);
}
return Array
.from(floorTravelList)
.sort(getTravelPrecedence);
}
const currentFloorNumber = 3;
const selectedFloorNumbers = [2, 5, 4, 1];
const upwardFloorTravelSchedule =
getFloorTravelSchedule('up', currentFloorNumber, selectedFloorNumbers);
const downwardFloorTravelSchedule =
getFloorTravelSchedule('down', currentFloorNumber, selectedFloorNumbers);
console.log({
currentFloorNumber,
selectedFloorNumbers,
upwardFloorTravelSchedule,
downwardFloorTravelSchedule,
});
console.log(
"getFloorTravelSchedule('up', 3, [10, 4, 8, 1, -2, 6]) ...",
getFloorTravelSchedule('up', 3, [10, 4, 8, 1, -2, 6])
);
console.log(
"getFloorTravelSchedule('down', 3, [10, 4, 8, 1, -2, 6]) ...",
getFloorTravelSchedule('down', 3, [10, 4, 8, 1, -2, 6])
);
console.log(
"getFloorTravelSchedule('up', 7, [10, 4, 8, 1, -2, 6]) ...",
getFloorTravelSchedule('up', 7, [10, 4, 8, 1, -2, 6])
);
console.log(
"getFloorTravelSchedule('down', 0, [10, 4, 8, 1, -2, 6]) ...",
getFloorTravelSchedule('down', 0, [10, 4, 8, 1, -2, 6])
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
首先,欢迎来到 Whosebug!我通常对新手的建议是,你尝试 tour, visit the help center and read up on asking good questions. After doing some research and searching for related topics on SO, try it yourself. If you're stuck, post a minimal, reproducible example 并准确记下你被困的地方。
但是这里已经有了答案,我不会等待,而是会提出我自己的建议。
我们可以认为电梯有状态。我们可以应用使用该状态的函数来获得新状态。出于我们的目的,状态可能包括电梯的当前楼层、运行方向以及预定停靠点列表。
我们正在寻找一种功能,它可以乘坐静止的电梯,但已经选择了方向,并且可以访问一组楼层;它应该 return 整个新状态。如果我们只需要预定停靠站列表,我们可以从这个状态读取它。
我们可以通过调用电梯到给定楼层的函数来构建它,只需将楼层列表和初始状态折叠成新状态即可。所以 addStops
将取决于 call
-- 这本身对于将电梯呼叫到给定楼层很有用。
在任何一种情况下,我们都需要根据我们当前的楼层和方向对要设置的止损进行分类。所以也有一个功能。把这些放在一起,我们可以想象这样一个设计:
const sortStops = (current, direction, stops) => {
const floors = [... stops] .sort ((a, b) => a - b)
const above = floors .filter ((f) => f > current)
const below = floors .filter ((f) => f < current) .reverse ()
return direction == 'UP' ? [... above, ...below] : [...below, ...above]
}
const call = ({current, direction, stops}, floor) => {
const newStops = sortStops ([... new Set ([...stops, floor])], direction, current)
const newDirection = newStops .length ? (newStops [0] > current ? 'UP' : 'DOWN') : direction
return {current, direction: newDirection, stops: newStops}
}
const addStops = ({current, direction, stops}, floors) =>
sortStops (current, direction, stops) .reduce (call, elevator)
// our initial elevator state: on floor 3, headed up, with no stops scheduled
const elevator = {current: 3, direction: 'UP', stops: []}
console .log (addStops (elevator, [2, 5, 4, 1]))
这将 return
{current: 3, direction: "UP", stops: [4, 5, 2, 1]}
这样做的好处是我们可以在同一个数据结构上编写其他函数来执行电梯的其他操作,比如让它下一站:
const stop = ({current, stops, direction}) => stops .length
? {
current: stops [0],
stops: stops .slice (1),
direction: stops .length > 1 ? stops [1] > stops [0] ? 'UP' : 'DOWN' : 'N/A'
}
: {current, stops, direction: 'N/A'}
const elevator = {current: 3, stops: [], direction: 'UP'}
如果我们在 {current:3, direction: "UP", stops:[4,5,2,1]}
上调用 stop
,我们将得到
{current:4, direction: "UP", stops:[5,2,1]}
如果我们再次调用该结果,我们将得到
{current:5, direction: "DOWN", stops:[2,1]}
然后
{current:2, direction: "DOWN", stops:[1]}
和
{current:1, direction: "N/A", stops:[]}
此时另一个调用不会执行任何操作,return再次调用:
{current:1, direction: "N/A", stops:[]}
我们还可以编写其他函数 -- 也许是 cancelAllStops
以防出现火警。关键是我们可以使用一些简单的函数来处理不可变状态对象。在 object-oriented 系统中,这些将是可变对象上的方法。在这里它们只是简单的函数。这是构建数据的一种非常有用的方法。
楼层列表可以分为两组,一组包含当前楼层以上的所有楼层,另一组包含当前楼层以下的所有楼层。
楼层较高的群组,无论方向如何,始终需要按升序排序。
包含较低楼层的群组,无论方向如何,始终需要按降序排序。
如果方向是“向上”,则高层组需要出现在低层组之前。
如果方向是“DOWN”,那么低层组需要出现在高层组之前。
function getRoute(dir, currFloor, floorList) {
return floorList
.reduce((g, f) => (g[+(dir === "UP" ? f < currFloor : f > currFloor)].push(f), g), [[], []])
.flatMap((g, i) => g.sort((a, b) => (dir === "UP" ? 2 * i - 1 : 1 - 2 * i) * (b - a)));
}
console.log(getRoute("UP", 3, [5, 1, 4, 7, 2, 0]));
console.log(getRoute("DOWN", 3, [5, 1, 4, 7, 2, 0]));
console.log(getRoute("UP", 5, [1, 0, 2]));
console.log(getRoute("UP", 4, [7, 9, 5, 10]));
console.log(getRoute("DOWN", 5, [1, 0, 2]));
console.log(getRoute("DOWN", 4, [7, 9, 5, 10]));
.as-console-wrapper { min-height: 100% !important; top: 0; }
使用Array.prototype.reduce将floorList
分组,根据dir
决定哪一组先出现。
使用Array.prototype.flatMap对较高楼层组进行升序排序,对较低楼层组进行降序排序。
我正在用 React 制作电梯,但我需要制作一个函数,将数组排序到最接近数字 X 的位置,并且还有电梯上升或下降的条件,
例如,
- 共有5层。
- X = 当前楼层 3
您现在在 3 楼,点击按钮 UP 然后点击数字 2 -> 5 -> 4 -> 1
数组应该按如下方式排序:3 -> 4 -> 5 -> 2 -> 1 .
伪代码:
let currentFloor = 3;
let direction = "UP";
let clickedButtons = [2,5,4,1];
// ClickedButtons after sorted
clickedButtons = [4,5,2,1]
您可以将数组分成两个数组。一个仅包含比 currentFloor 更高的数字,另一个仅包含更低的数字,例如:
let lowerFloors = clickedButtons.filter(floor => floor > currnentFloor);
let upperFloors = clickedButtons.filter(floor => floor > currnentFloor);
然后对每个数组进行排序,lowerFloors 为降序,higherFloors 为升序。
upperFloors = upperFloors.sort((a, b) => {
return a - b;
});
lowerFloors = lowerFloors.sort((a, b) => {
return a - b;
});
lowerFloors = lowerFloors.reverse();
然后合并两个数组。如果你上升 upperFloors 数组将是第一个,否则 lowerFloors 将是第一个。
let newArray;
if (direction === "UP") {
newArray = upperFloors.concat(lowerFloors);
} else {
newArray = lowerFloors.concat(upperFloors)
}
我猜你需要这样的东西
无论何时上楼,您都需要找到所有更大的数字并将它们按升序排序,以便电梯在上升时停在下一个可能的楼层。
然后找出所有较小的数字并倒序排列。
如果选择向下,将执行相反的过程。
let arr = [10, 4, 8, 1, -2, 6];
let x = 3;
const goUp = (num, arr) => arr.filter(obj => obj > num).sort((a, b) => a - b)
const goDown = (num, arr) => arr.filter(obj => obj < num).sort((a, b) => b - a)
function printSequence(arr, initial, direction) {
if (direction === 'up') {
console.log(goUp(initial, arr).concat(goDown(initial, arr)))
} else {
console.log(goDown(initial, arr).concat(goUp(initial, arr)))
}
}
printSequence(arr, 3, 'up')
printSequence(arr, 3, 'down')
根据我上面的两条评论...
@Cedric ... 1/2 With this scenario the only sorting needed is e.g. an ascending sorting of all floor numbers ...
[1, 2, 3, 4, 5]
... one find's the index of the current floor number3
which is2
...'up'
will be translated into direction vector of1
, thus one takes 1st everything higher than index2
or right from index2
... and 2nd everything left from index2
... result ...[4, 5, 2, 1]
.@Cedric ... 2/2 One does likewise for
'down'
/-1
where one 1st takes everything left from index2
and 2nd everything right from index2
... result ...[2, 1, 4, 5]
. There is no sorting magic since with the known preset of either'up'
or'down'
the floor travel schedule is obvious.
...但是人们当然可以将我的评论的命令式配方转化为单一排序公式。
function getFloorTravelSchedule(
direction = 'up',
floorNumber = 0,
floorTravelList = [],
) {
direction = ((direction === 'up') && 1) || -1;
function getTravelPrecedence(a, b) {
return (
(a > floorNumber && b > floorNumber && a - b) ||
(a < floorNumber && b < floorNumber && b - a) ||
((a > floorNumber && b < floorNumber && -1) || 1) * direction
);
}
return Array
.from(floorTravelList)
.sort(getTravelPrecedence);
}
const currentFloorNumber = 3;
const selectedFloorNumbers = [2, 5, 4, 1];
const upwardFloorTravelSchedule =
getFloorTravelSchedule('up', currentFloorNumber, selectedFloorNumbers);
const downwardFloorTravelSchedule =
getFloorTravelSchedule('down', currentFloorNumber, selectedFloorNumbers);
console.log({
currentFloorNumber,
selectedFloorNumbers,
upwardFloorTravelSchedule,
downwardFloorTravelSchedule,
});
console.log(
"getFloorTravelSchedule('up', 3, [10, 4, 8, 1, -2, 6]) ...",
getFloorTravelSchedule('up', 3, [10, 4, 8, 1, -2, 6])
);
console.log(
"getFloorTravelSchedule('down', 3, [10, 4, 8, 1, -2, 6]) ...",
getFloorTravelSchedule('down', 3, [10, 4, 8, 1, -2, 6])
);
console.log(
"getFloorTravelSchedule('up', 7, [10, 4, 8, 1, -2, 6]) ...",
getFloorTravelSchedule('up', 7, [10, 4, 8, 1, -2, 6])
);
console.log(
"getFloorTravelSchedule('down', 0, [10, 4, 8, 1, -2, 6]) ...",
getFloorTravelSchedule('down', 0, [10, 4, 8, 1, -2, 6])
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
首先,欢迎来到 Whosebug!我通常对新手的建议是,你尝试 tour, visit the help center and read up on asking good questions. After doing some research and searching for related topics on SO, try it yourself. If you're stuck, post a minimal, reproducible example 并准确记下你被困的地方。
但是这里已经有了答案,我不会等待,而是会提出我自己的建议。
我们可以认为电梯有状态。我们可以应用使用该状态的函数来获得新状态。出于我们的目的,状态可能包括电梯的当前楼层、运行方向以及预定停靠点列表。
我们正在寻找一种功能,它可以乘坐静止的电梯,但已经选择了方向,并且可以访问一组楼层;它应该 return 整个新状态。如果我们只需要预定停靠站列表,我们可以从这个状态读取它。
我们可以通过调用电梯到给定楼层的函数来构建它,只需将楼层列表和初始状态折叠成新状态即可。所以 addStops
将取决于 call
-- 这本身对于将电梯呼叫到给定楼层很有用。
在任何一种情况下,我们都需要根据我们当前的楼层和方向对要设置的止损进行分类。所以也有一个功能。把这些放在一起,我们可以想象这样一个设计:
const sortStops = (current, direction, stops) => {
const floors = [... stops] .sort ((a, b) => a - b)
const above = floors .filter ((f) => f > current)
const below = floors .filter ((f) => f < current) .reverse ()
return direction == 'UP' ? [... above, ...below] : [...below, ...above]
}
const call = ({current, direction, stops}, floor) => {
const newStops = sortStops ([... new Set ([...stops, floor])], direction, current)
const newDirection = newStops .length ? (newStops [0] > current ? 'UP' : 'DOWN') : direction
return {current, direction: newDirection, stops: newStops}
}
const addStops = ({current, direction, stops}, floors) =>
sortStops (current, direction, stops) .reduce (call, elevator)
// our initial elevator state: on floor 3, headed up, with no stops scheduled
const elevator = {current: 3, direction: 'UP', stops: []}
console .log (addStops (elevator, [2, 5, 4, 1]))
这将 return
{current: 3, direction: "UP", stops: [4, 5, 2, 1]}
这样做的好处是我们可以在同一个数据结构上编写其他函数来执行电梯的其他操作,比如让它下一站:
const stop = ({current, stops, direction}) => stops .length
? {
current: stops [0],
stops: stops .slice (1),
direction: stops .length > 1 ? stops [1] > stops [0] ? 'UP' : 'DOWN' : 'N/A'
}
: {current, stops, direction: 'N/A'}
const elevator = {current: 3, stops: [], direction: 'UP'}
如果我们在 {current:3, direction: "UP", stops:[4,5,2,1]}
上调用 stop
,我们将得到
{current:4, direction: "UP", stops:[5,2,1]}
如果我们再次调用该结果,我们将得到
{current:5, direction: "DOWN", stops:[2,1]}
然后
{current:2, direction: "DOWN", stops:[1]}
和
{current:1, direction: "N/A", stops:[]}
此时另一个调用不会执行任何操作,return再次调用:
{current:1, direction: "N/A", stops:[]}
我们还可以编写其他函数 -- 也许是 cancelAllStops
以防出现火警。关键是我们可以使用一些简单的函数来处理不可变状态对象。在 object-oriented 系统中,这些将是可变对象上的方法。在这里它们只是简单的函数。这是构建数据的一种非常有用的方法。
楼层列表可以分为两组,一组包含当前楼层以上的所有楼层,另一组包含当前楼层以下的所有楼层。
楼层较高的群组,无论方向如何,始终需要按升序排序。
包含较低楼层的群组,无论方向如何,始终需要按降序排序。
如果方向是“向上”,则高层组需要出现在低层组之前。
如果方向是“DOWN”,那么低层组需要出现在高层组之前。
function getRoute(dir, currFloor, floorList) {
return floorList
.reduce((g, f) => (g[+(dir === "UP" ? f < currFloor : f > currFloor)].push(f), g), [[], []])
.flatMap((g, i) => g.sort((a, b) => (dir === "UP" ? 2 * i - 1 : 1 - 2 * i) * (b - a)));
}
console.log(getRoute("UP", 3, [5, 1, 4, 7, 2, 0]));
console.log(getRoute("DOWN", 3, [5, 1, 4, 7, 2, 0]));
console.log(getRoute("UP", 5, [1, 0, 2]));
console.log(getRoute("UP", 4, [7, 9, 5, 10]));
console.log(getRoute("DOWN", 5, [1, 0, 2]));
console.log(getRoute("DOWN", 4, [7, 9, 5, 10]));
.as-console-wrapper { min-height: 100% !important; top: 0; }
使用Array.prototype.reduce将
floorList
分组,根据dir
决定哪一组先出现。使用Array.prototype.flatMap对较高楼层组进行升序排序,对较低楼层组进行降序排序。