I have an array with days in it. Each day is an object, for example:
{day_year: "2012", day_month: "08", day_number: "03", day_name: "mon"}
I have also added a timestamp attribute to each day object, by using:
function convertDays() {
var max_i = days.length;
for(var i = 0; i < max_i; i++) {
var tar_i = days[i];
tar_i.timestamp = new Date(tar_i.day_year, tar_i.day_month, tar_i.day_number);
}
}
The days in the array are arbitrary, so there is no real logic to them.
Now I want to find the two closest days to any given date. So if the array with days contains
- August 2, 2012
- August 4, 2012
- August 23, 2012
And I search for August 11, 2012, I want it to return August 4, 2012 and August 23, 2012.
I have tried using an answer from another question, that looks like this:
function findClosest(a, x) {
var lo, hi;
for(var i = a.length; i--;) {
if(a[i] <= x && (lo === undefined || lo < a[i])) lo = a[i];
if(a[i] >= x && (hi === undefined || hi > a[i])) hi = a[i];
}
return [lo, hi];
}
However, this returns unidentified
.
What would be the most efficient (least processor/memory intensive way) to achieve this?
Edit: “However, how are those results “strange”? Could you provide an example of your code and data?”
I’m now using the following to generate an array of dates:
var full_day_array = [];
for(var i = 0; i < 10; i++) {
var d = new Date();
d.setDate(d.getDate() + i);
full_day_array.push({day_year: d.getFullYear().toString(), day_month: (d.getMonth() + 1).toString(), day_number: d.getDate().toString()});
}
The strange part is, using the code below, this only works for an array of 10 dates or shorter. Whenever I use an array of 11 or more dates, the results become unexpected.
For instance: using an array of 15 dates, starting on August 6, 2012, to August 21, 2012. If I then call findClosest(full_day_array, new Date("30/07/2012");
you would expect it to return {nextIndex: 0, prevIndex: -1}
. However, it returns {nextIndex: 7, prevIndex: -1}
. Why?
function findClosest(objects, testDate) {
var nextDateIndexesByDiff = [],
prevDateIndexesByDiff = [];
for(var i = 0; i < objects.length; i++) {
var thisDateStr = [objects[i].day_month, objects[i].day_number, objects[i].day_year].join('/'),
thisDate = new Date(thisDateStr),
curDiff = testDate - thisDate;
curDiff < 0
? nextDateIndexesByDiff.push([i, curDiff])
: prevDateIndexesByDiff.push([i, curDiff]);
}
nextDateIndexesByDiff.sort(function(a, b) { return a[1] < b[1]; });
prevDateIndexesByDiff.sort(function(a, b) { return a[1] > b[1]; });
var nextIndex;
var prevIndex;
if(nextDateIndexesByDiff.length < 1) {
nextIndex = -1;
} else {
nextIndex = nextDateIndexesByDiff[0][0];
}
if(prevDateIndexesByDiff.length < 1) {
prevIndex = -1;
} else {
prevIndex = prevDateIndexesByDiff[0][0];
}
return {nextIndex: nextIndex, prevIndex: prevIndex};
}
Как найти ближайшую дату в массиве?
Приходит массив из которого надо выбрать ближайшую дату по принципу “сегодня или позже”:
const Array = [
{
date: '01/01/2022',
id: '1111',
},
{
date: '31/03/2022',
id: '2222',
},
{
date: '23/05/2022',
id: '3333',
}
];
const findClosest = (data, accessor, target = Date.now()) =>
data.reduce((prev, curr) => {
const a = Math.abs(accessor(curr).getTime() - target);
const b = Math.abs(accessor(prev).getTime() - target);
return a - b < 0 ? curr : prev;
});
const processDateString = (dateString) => {
const [date, month, year] = dateString.split(///g).map(Number);
return new Date(year, month - 1, date);
};
const closest = findClosest(Array, ({ date }) => processDateString(date));
const closestDate = closest.date;
console.log(closestDate);
возвращает ближайшую, но в прошлом.
Подскажите как переписать на “сегодня или позже”?
-
Вопрос заданболее года назад
-
281 просмотр
/**
* My item definition
* @typedef {Object} myItem
* @property {string} date
* @property {string} id
*/
/**
* Find item with the date of today
* or the nearest one in the future
* @param {myItem[]} data
* @returns {?myItem}
*/
const getNearest = data => {
const now = new Date();
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const index = data
.map(({date}, orginalIndex) => {
const [d, m, y] = date.split('/');
return { sorter: new Date(y, m - 1, d), orginalIndex };
})
.sort((a, b) => a.sorter - b.sorter)
.find(item => item.sorter >= today)
?.orginalIndex;
return data[index] || null;
}
// использование
const data = [
{ date: '01/01/2022', id: '1' },
{ date: '31/03/2022', id: '2' },
{ date: '23/05/2022', id: '3' },
{ date: '05/04/2022', id: '4' },
{ date: '03/04/2022', id: '5' },
{ date: '09/04/2022', id: '6' },
];
getNearest(data) // { date: "03/04/2022", id: "5" }
Пригласить эксперта
const parseDate = (date) => Date.parse(date.split('/').reverse().join('-'));
const findClosest = (list, date = (new Date()).toLocaleDateString('en-GB')) => {
const findDate = parseDate(date);
return list.reduce(
(acc, cur) => {
const delta = parseDate(cur.date) - findDate;
return (delta >= 0 && delta < acc.delta) ? { delta, el: cur } : acc;
},
{ delta: Number.MAX_SAFE_INTEGER, el: null },
).el;
};
-
Показать ещё
Загружается…
19 мая 2023, в 14:02
100000 руб./за проект
19 мая 2023, в 13:22
7000 руб./за проект
11 мая 2023, в 16:20
1500 руб./в час
Минуточку внимания
У нас есть массив такого вида:
Array
(
[0] => 2016-02-22 00:20:00
[1] => 2016-02-25 08:45:00
[2] => 2016-02-25 19:10:00
[3] => 2016-02-25 20:00:00
[4] => 2016-02-26 15:55:00
[5] => 2016-02-28 17:10:00
)
Нужно из него выбрать ближайшую дату относительно текущего времени.
Например, если сейчас 25.02.2016-14:45
, то ближайшей будет 2016-02-25 08:45:00
VenZell
19.8k5 золотых знаков43 серебряных знака61 бронзовый знак
задан 26 фев 2016 в 12:43
4
Вот рабочий пример
<?php
$dates = [
'2016-02-22 00:20:00',
'2016-02-25 08:45:00',
'2016-02-25 19:10:00',
'2016-02-25 20:00:00',
'2016-02-26 15:55:00',
'2016-02-28 17:10:00'
];
$now = time();
$past_dates = [];
foreach ($dates as $date) {
$timestamp = strtotime($date);
if ($now >= $timestamp) {
$past_dates[] = $timestamp;
}
}
$result = 'unknown';
if (count($past_dates) > 0) {
$result = date('Y-m-d H:i:s', max($result));
}
echo $result;
ответ дан 26 фев 2016 в 13:00
VenZellVenZell
19.8k5 золотых знаков43 серебряных знака61 бронзовый знак
Пусть имеется диапазон с датами. Найдем дату из этого диапазона, которая является ближайшей к заданной. Решение этой задачи аналогично решению, изложенного в статье
Поиск ЧИСЛА ближайшего к заданному
.
Пусть в диапазоне
A4:A12
имеется список дат. Будем в нем искать дату из ячейки
С4
. Если диапазон не содержит искомого значения, то будет возвращено ближайшее значение.
Искомая дата необязательно должна совпадать с какой-нибудь датой или даже находиться в диапазоне поиска (см.
Файл примера
):
|
|
|
=
= |
ищется наибольшее значение, которое меньше, чем искомое значение (если искомая дата меньше, чем минимальное значение из диапазона, то будет возвращена ошибка #Н/Д) |
если столбец не отсортирован по возрастанию, то результат непредсказуем |
= |
ищется |
столбец м.б. не отсортирован |
= |
ищется |
столбец м.б. не отсортирован |
= |
ищется |
столбец м.б. не отсортирован |
= |
ищется |
столбец м.б. не отсортирован |
= |
ищется |
столбец м.б. не отсортирован |
Последние 5 решений реализованы с использованием
формул массива
. Для пошагового просмотра хода вычислений используйте клавишу
F9
.
Как видно из таблицы, применение функции
ВПР()
со значением аргумента
интервальный_просмотр
равным ИСТИНА, имеет недостатки. Во-первых, найденное значение м.б. далеко не ближайшим (задав в качестве критерия дату 06.02.2009 получим не ближайшую дату 07.02.2009, а наибольшее значение, которое меньше, чем искомое значение, т.е. 05.01.2009). Во-вторых, если искомая дата меньше, чем минимальное значение из диапазона, то будет возвращена ошибка #Н/Д. В-третьих, требуется
сортировка
списка, что не всегда удобно.
Хорошим решением является
формула массива
=ИНДЕКС(A4:A12; ПОИСКПОЗ(МИН(ABS(A4:A12-C4));ABS(A4:A12-C4);0))
свободная от всех указанных недостатков, но имеющая свои: формула относительно сложная и является
формулой массива
.
Совет
: т.к. дата в MS EXCEL хранится в числовом виде (см. статью
Как Excel хранит дату и время
), то формулы для поиска ближайшего числа также будут работать для дат (см. раздел
Ближайшее ЧИСЛО
).
#php #arrays #date
#php #массивы #Дата
Вопрос:
$dates[] = array("date" => "2016-02-18 02:00:00", "duration" => "600");
$dates[] = array("date" => "2016-02-18 02:05:00", "duration" => "300");
$dates[] = array("date" => "2016-02-18 02:10:00", "duration" => "600");
$dates[] = array("date" => "2016-02-18 02:25:00", "duration" => "300");
$dates[] = array("date" => "2016-02-18 02:30:00", "duration" => "600");
function closestDates($array, $date){
foreach($array as $day)
$interval[] = abs(strtotime($date["date"]) - strtotime($day["date"]));
asort($interval);
$closest = key($interval);
$alreadyChosen[] = $array[$closest];
return $alreadyChosen;
}
$returnedDates = closestDates($dates, array("date" => "2016-02-18 02:00:00", "duration" => "600"));
print_r($returnedDates);
// This returns
Array (
[0] => Array (
[date] => 2016-02-18 02:00:00
[duration] => 600
)
)
Как я могу адаптировать вышеуказанную функцию, чтобы иметь возможность проверять dates
массив и помещать следующую ближайшую дату в alreadyChosen
массив на основе времени продолжительности.
$dates = (
'0'=> array("date" => "2016-02-18 02:00:00", "duration" => "600"),
'1'=> array("date" => "2016-02-18 02:05:00", "duration" => "300"),
'2'=> array("date" => "2016-02-18 02:10:00", "duration" => "600"),
'3'=> array("date" => "2016-02-18 02:25:00", "duration" => "300"),
'4'=> array("date" => "2016-02-18 02:30:00", "duration" => "600")
);
// Expected result to be after the checks:
$alreadyChosen = array
(
'0'=> array("date" => "2016-02-18 02:00:00", "duration" => "600"),
'1'=> array("date" => "2016-02-18 02:10:00", "duration" => "600"),
'2'=> array("date" => "2016-02-18 02:25:00", "duration" => "300"),
'3'=> array("date" => "2016-02-18 02:30:00", "duration" => "600")
);
Комментарии:
1. Я считаю, что функция closestDate уже работает нормально …. Теперь, как вы хотите использовать $ alreadyChosen??? для меня это непонятно в вашем вопросе
2. что вы подразумеваете под
doesn't check on duration time
. Также в вашем вопросе упоминаются два$alreadyChosen
3. не могли бы вы подробнее рассказать о своем ожидаемом результате.. Я не могу ее получить
4. @undefined_variable Ожидаемый результат — иметь возможность передавать начальную дату, которая была бы
2016-02-18 02:00:00
длительностью 600 (10 минут) в зависимости от продолжительности, найдите ближайшую дату, которая была бы равна концу продолжительности или больше продолжительности. Затем так далее…..5. @undefined_variable Я обновил свой вопрос, теперь его должно быть легче понять.
Ответ №1:
Если вы хотите получить наиболее близкую дату к дате, которую вы вводите в функцию, и предотвратить даты, которые уже выбраны, вы могли бы сделать это так:
function pickMostCloserDate($dateArray, $compareDate, amp;$pickedDates = array())
{
$dates = [];
foreach($dateArray as $key => $originalDate){
$date = $date['date'];
//compare options
if(!in_array($originalDate, $pickedDates)){
$dates[$key] = abs(strtotime($compareDate) - strtotime($date));
}
}
asort($dates);
$dateIndex = array_shift(array_keys($dates));
array_push($pickedDates, $dateArray[$dateIndex]);
return $dateArray[$dateIndex];
}
Это вернет самую близкую дату и добавит найденную дату в выбранные даты, так что на самом деле вам нужно сделать это:
$pickedDates = [];
$closeDate = pickMostCloserDate($arrayWithDates, '2016-05-05 00:00:00', $pickedDates);
echo $CloseDate;
// следующий раунд для выбора даты
Надеюсь, это поможет. Пожалуйста, имейте в виду, что я написал это из head, нужно исправить.
Комментарии:
1. Привет, я обновил свой вопрос дополнительной информацией, это должно помочь вам с тем, что я ищу.
2. Я еще больше обновил свой ответ, теперь его должно быть легче понять.
3. Привет, Крис, это должно помочь вам с вашей проблемой. Функция выберет ближе к определенной дате, а также добавит найденную дату в переменную pickedDates. По мере того, как вы будете вызывать функцию, будут выбираться новые даты.
4. Привет, Камил. Ваш ответ пока дает мне именно то, что я уже получил в своем вопросе. Пожалуйста, проверьте мой ожидаемый результат, чтобы дать себе представление о том, что я ищу.
5. Крис, я не уверен, понимаю ли я, чего ты хочешь. Но, как я вижу, во втором массиве больше дат. Что на самом деле делает эта функция, если вы будете вызывать функцию несколько раз. Он добавит даты в переменную $pickedDates. Таким образом, в этом случае вы, вероятно, могли бы поместить эту функцию в цикл, чтобы сохранить эти даты в этой переменной. Это то, что я понял, чего вы хотели.
Ответ №2:
Я нашел решение, выполнив следующее: (я многое изменил в своем вопросе, чтобы заставить его работать)
$dates[] = array("date" => "2016-02-18 02:00:00", "meeting_id" => "1", "class_id" => "10", "duration" => "600");
$dates[] = array("date" => "2016-02-18 02:10:00", "meeting_id" => "1", "class_id" => "10", "duration" => "600");
$dates[] = array("date" => "2016-02-18 02:20:00", "meeting_id" => "1", "class_id" => "10", "duration" => "600");
$dates[] = array("date" => "2016-02-18 02:30:00", "meeting_id" => "1", "class_id" => "10", "duration" => "600");
$dates[] = array("date" => "2016-02-18 02:10:00", "meeting_id" => "2", "class_id" => "10", "duration" => "600");
$dates[] = array("date" => "2016-02-18 02:25:00", "meeting_id" => "2", "class_id" => "10", "duration" => "300");
$dates[] = array("date" => "2016-02-18 02:30:00", "meeting_id" => "3", "class_id" => "10", "duration" => "600");
$dates[] = array("date" => "2016-02-18 02:40:00", "meeting_id" => "3", "class_id" => "10", "duration" => "600");
$dates[] = array("date" => "2016-02-18 02:30:00", "meeting_id" => "4", "class_id" => "11", "duration" => "600");
$dates[] = array("date" => "2016-02-18 02:40:00", "meeting_id" => "4", "class_id" => "11", "duration" => "600");
$dates[] = array("date" => "2016-02-18 02:50:00", "meeting_id" => "4", "class_id" => "11", "duration" => "600");
$dates[] = array("date" => "2016-02-18 03:00:00", "meeting_id" => "4", "class_id" => "11", "duration" => "600");
$firstDates[] = array("date" => "2016-02-18 02:00:00", "meeting_id" => "1", "class_id" => "10", "duration" => "600");
$firstDates[] = array("date" => "2016-02-18 02:10:00", "meeting_id" => "1", "class_id" => "10", "duration" => "600");
$firstDates[] = array("date" => "2016-02-18 02:20:00", "meeting_id" => "1", "class_id" => "10", "duration" => "600");
$firstDates[] = array("date" => "2016-02-18 02:30:00", "meeting_id" => "1", "class_id" => "10", "duration" => "600");
$children[] = array("id" => 1, "class_id" => "10", "fullname" => "Callum");
$children[] = array("id" => 2, "class_id" => "10", "fullname" => "Daniel");
$children[] = array("id" => 3, "class_id" => "11", "fullname" => "Jake");
function dateExists($array, $child, $date) {
if (empty($array)) {
return false;
}
$flag = false;
foreach($array as $value) {
if ($value['child']['id'] == $child['id'] amp;amp; $value['meeting']['meeting_id'] === $date['meeting_id']){
return true;
}
if ($value['meeting']['date'] == $date['date']) {
return true;
}
$start = strtotime($value['meeting']['date']);
$end = $start $value['meeting']['duration'];
$ts = strtotime($date['date']);
if ($ts > $start amp;amp; $ts < $end) {
$flag = true;
break;
}
}
return $flag;
}
$results = [];
function buildDates($dates, $children, $key, $firstDate) {
$temp = [];
$pickedDates = array();
foreach ($children as $child) {
if($firstDate["class_id"] === $child["class_id"]) {
$temp[] = array('child' => $child, 'meeting' => $firstDate);
$pickedDates[$firstDate['date']] = $temp;
}
break;
}
foreach ($dates as $key => $date) {
foreach ($children as $child) {
if($date["class_id"] === $child["class_id"]) {
if (!dateExists($temp, $child, $date)) {
$temp[] = array('child' => $child, 'meeting' => $date);
$pickedDates[$firstDate['date']] = $temp;
}
}
}
}
return $pickedDates = array($firstDate["date"] => $temp);
}
foreach ($firstDates as $date) {
$results[] = buildDates($dates, $children, $date);
}
print_r($results);