Как сгенерировать конкретный массив строк? - PullRequest
2 голосов
/ 19 июня 2019

У меня следующая проблема. У меня есть массив:

const dates = ["19-June-2019", "18-June-2019", "17-June-2019", "16-June-2019", "14-June-2019"]

Мне нужно сгенерировать массив дат после того, как я имею в виду даты в строке. Итак:

const datesInRow = ["19-June-2019", "18-June-2019", "17-June-2019", "16-June-2019"]

Вот моя начальная функция: (isAfter function = date-fns)

 function numberOfDaysInRow(arr) {
  if (arr.length === 1) return arr;
  let numberOfDaysInRowArr = [];
  for (let i = 0; i < arr.length; i++) {
    if (isAfter(new Date(arr[i]), new Date(arr[i + 1]))) {
      numberOfDaysInRowArr.push(arr[i]);
    }
  }
  return numberOfDaysInRowArr;
}

Это возвращает только частичный ответ. Например, если у меня есть только две строки в массиве, как это:

const dates = ["19-June-2019", "18-June-2019"]

Он вернется

["19-June-2019"]

это не то, что я хочу.

Ответы [ 6 ]

2 голосов
/ 19 июня 2019

const dates = ["19-June-2019", "18-June-2019", "17-June-2019", "16-June-2019", "14-June-2019"]
const datesInRow = ["19-June-2019", "18-June-2019", "17-June-2019", "16-June-2019"]
const datesBroken = ["19-June-2019", "18-June-2019", "17-June-2019", "16-June-2019", "10-June-2019", "09-June-2019", "08-June-2019"]

function isAfter(date1, date2) {
  return (date1.getTime() - date2.getTime()) == 86400000;
}

function numberOfDaysInRow(arr) {
  if (arr.length === 1) return arr;
  let numberOfDaysInRowArr = [arr[0]];
  for (let i = 0; i < arr.length - 1; i++) {
    if (isAfter(new Date(arr[i]), new Date(arr[i + 1]))) {
      numberOfDaysInRowArr.push(arr[i + 1]);
    } else {
      i = arr.length - 1;
    }
  }
  return numberOfDaysInRowArr;
}

console.log(numberOfDaysInRow(dates))
console.log(numberOfDaysInRow(datesInRow))
console.log(numberOfDaysInRow(datesBroken))

Вы должны инициализировать numberOfDaysInRowArr с [arr[0]].

И выполнить цикл i до arr.length - 1.

Также нажмите arr[i + 1] вместо arr[i].

2 голосов
/ 19 июня 2019

Вы можете обработать первый элемент вне цикла и взять только следующий элемент.

function numberOfDaysInRow(arr) {
    if (arr.length === 1) return arr;
    let numberOfDaysInRowArr = [];

    if (isAfter(new Date(arr[0]), new Date(arr[1]))) {
        numberOfDaysInRowArr.push(arr[0]);
    }

    for (let i = 0; i < arr.length; i++) {
        if (isAfter(new Date(arr[i]), new Date(arr[i + 1]))) {
            numberOfDaysInRowArr.push(arr[i + 1]);
        }
    }
    return numberOfDaysInRowArr;
}

Абстрактный подход с использованием переменной state со следующими значениями:

  • 0: начальное значение или следующая дата не найдена
  • 1: найдена следующая дата
  • 2: конец последовательности

Этот подход принимает целочисленные значения вместо даты, когда порядок возрастает, а не убывает.

Требуется только первый найденный непрерывный набор данных.

var array = [1, 3, 5, 7, 8, 9, 10, 12, 14],
    state = 0,
    result = array.filter((v, i, { [i - 1]: prev, [i + 1]: next }) => {
        switch (state) {
            case 0:
                if (v + 1 === next) {
                    state = 1;
                    return true;
                }
                break;
            case 1:
                if (prev + 1 === v) return true;
                state = 3;
                break
        }
    });

console.log(result);
1 голос
/ 20 июня 2019

Вы не должны использовать встроенный анализатор для отметок времени, так как формат DD-MMM-YYYY не поддерживается ECMA-262.Кроме того, функция date-fns isAfter просто сообщает вам, находится ли первая дата после второй, но не сообщает вам, насколько.Более подходящей функцией была бы diffInDays .

date-fns не имеет своего собственного анализатора, он использует встроенный анализатор, поэтому я бы предложил использоватьдругая библиотека.Парсеры были рассмотрены много раз в других вопросах, но парсер для формата OP не сложен.

Так что просто начните с первой даты и остановитесь, когда вы либо доберетесь до конца массива, либо получитедата, которая более чем за один день до предыдущей даты, например

// Parser for format DD-MMM-YYYY
// Does not validate date
function parseDMY(s) {
  let months = 'jan feb mar apr may jun jul aug sep oct nov dec'.split(' ');
  let b = s.split('-');
  return new Date(b[2], months.indexOf(b[1].toLowerCase().substr(0,3)), b[0]);
}

// Return true if d1 is the day before d0
// where d0, d1 are Dates
function isPreviousDay(d0, d1) {
  // Copy dates and set to 00:00:00
  let t0 = new Date(d0);
  t0.setHours(0,0,0,0);
  let t1 = new Date(d1);
  t1.setHours(0,0,0,0);
  // Set t0 to previous day
  t0.setDate(t0.getDate() - 1);
  // See if they now have the same time value
  return t0.getTime() == t1.getTime();
}

function getContiguous(dates) {
  // Get timestamps as array of Dates
  let arr = dates.map(parseDMY);
  // Result starts with first timestamp
  let result = [dates[0]];
  // Stop comparing at second last date
  let len = dates.length - 2
  let i = 0;

  // While each date is the day before the current date,
  // add its timestamp to the result array
  while (i<len && isPreviousDay(arr[i], arr[i+1])) {
    result.push(dates[i+1]);
    i++;
  }
  return result;
}

let dates = ["19-June-2019", "18-June-2019", "17-June-2019", "16-June-2019", "14-June-2019"];

console.log(getContiguous(dates));
<script src="https://cdnjs.cloudflare.com/ajax/libs/date-fns/1.30.1/date_fns.min.js"></script>

Я подключил библиотеку date-fns, но она не загружалась правильно, поэтому я написал сделанную на заказ функцию isPreviousDay .

1 голос
/ 19 июня 2019

Вы можете проверить, является ли ваш последний элемент ...

function numberOfDaysInRow(arr) {
  if (arr.length === 1) return arr;
  let numberOfDaysInRowArr = [];
  for (let i = 0; i < arr.length; i++) {
    const isTheLastElement = i === arr.length - 1;
    const isAfterNext = () => isAfter(new Date(arr[i]), new Date(arr[i + 1])); 
    if (isTheLastElement || isAfterNext()) {
      numberOfDaysInRowArr.push(arr[i]);
    }
  }
  return numberOfDaysInRowArr;
}

Теперь, если вы извлекаете условие (предикат), вы можете использовать filter, чтобы написать более чистую версию ...

function isLastElementOrAfterNext(string, index, stringList) {
   return index === stringList.length - 1 || isAfter(new Date(string), new Date(stringList[index + 1];
}
function numberOfDaysInRow(arr) {
   return arr.filter(isLastElementOrAfterNext);
}

Вы можете улучшить этот пример, используя сначала map, чтобы преобразовать строки в даты ...

function stringToDate(string) {
   return new Date(string);
}
function isLastElementOrAfterNext(date, index, dateList) {
   return index === dateList.length - 1 || isAfter(date, dateList[index + 1];
}
function numberOfDaysInRow(arr) {
   return arr.map(stringToDate).filter(isLastElementOrAfterNext);
}
1 голос
/ 19 июня 2019

Каждую дату можно сравнить с предыдущей и следующей датой, чтобы проверить, не разделены ли они днем:

const dates = ["6-June-2019", "4-June-2019", "3-June-2019", "1-June-2019"]

const filtered = dates.filter((v, i, a) => new Date(v) - new Date(a[i - 1]) > -1e8
                                        || new Date(v) - new Date(a[i + 1]) <  1e8 )

console.log( filtered )

Для обработки нескольких групп и получения самой большой из них:

const dates = ["23-June-2019", "22-June-2019", 
               "13-June-2019", "12-June-2019", "11-June-2019", 
                                "2-June-2019",  "1-June-2019"];

let start = 0, maxCount = 0;

for (let count = 1, i = dates.length - 1; i >= 0; i--, count++) {
  if (i < 1 || new Date(dates[i - 1]) - new Date(dates[i]) > 1e8) {
    if (count > maxCount) {
      start = i; 
      maxCount = count;
    }
    count = 0; 
  }
}

console.log( dates.slice(start, start + maxCount) );

Чтобы найти первую группу:

const dates = ["23-June-2019", 
               "13-June-2019", "12-June-2019", 
                "3-June-2019",  "2-June-2019",  "1-June-2019"];

const start = dates.findIndex((v, i, a) => new Date(v) - new Date(a[++i]) < 1e8);
const end = dates.findIndex((v, i, a) => i > start && new Date(v) - new Date(a[++i]) > 1e8);

console.log( dates.slice(start, end + 1) );
0 голосов
/ 19 июня 2019
if (isAfter(new Date(arr[i]), new Date(arr[i + 1]))) {
  numberOfDaysInRowArr.push(arr[i]);
}

Если arr [i] является последним элементом в массиве, arr [i + 1] будет неопределенным, и ничто не будет помещено в numberOfDaysInRowArr.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...