如何将日期压缩到他们的月份?

时间:2019-06-26 13:30:27

标签: javascript arrays date momentjs slice

我需要将日期数组压缩到他们的月份。

我想使用slice方法和库moment.js,将数组压缩到它们的月份。

在这里,我得到了数组当前日期的月份的最后日期。

//Variables
$Host = 'localhost';
$UserName = 'root';
$Password = 'NOP';
$DataBaseName = 'BoosTemplatesDB';
$DEBUG = True;

$link = mysqli_connect($Host, $UserName, $Password, $DataBaseName);
$query_items = 'SELECT * FROM Products WHERE ID = 6';
$result      = $link->query($query_items);

if (mysqli_num_rows($result) > 0) {
    // Fetch one and one row
    while ($row = $result->fetch_assoc()) {
        echo $row[1];
    }
} else {
    echo 'No result found';
}

给出一个带有日期和数据的数组。我需要把它压缩到几个月。

const dateString = moment (lastIndexTemp, "DD.MM.YYYY")
        .endOf ("month")
        .format ("DD.MM.YYYY");

预期结果:

const dates   = [
  { date: "26.06.2019", someData: "foo" },
  { date: "27.06.2019", someData: "foo" },
  { date: "28.06.2019", someData: "foo" },
  { date: "29.06.2019", someData: "foo" },
  { date: "30.06.2019", someData: "foo" },
  { date: "01.07.2019", someData: "foo" },
  { date: "02.07.2019", someData: "foo" },
  { date: "03.07.2019", someData: "foo" },
  { date: "04.07.2019", someData: "foo" },
  { date: "05.07.2019", someData: "foo" },
  { date: "06.07.2019",someData: "foo" },
   ... 
  { date: "08.08.2019",someData: "foo" }
];

感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

让我们将此任务分解为单独的部分:

将日期字符串转换为momentjs对象

由于输入数据中的字符串表示日期,因此在进行其他操作之前先将其转换。我定义了一个辅助函数,以构造将字符串解析为片刻的新对象:

const DateEntry = ({ date, someData }) => ({
  date: moment(date, "DD.MM.YYYY"),
  data: someData
});

const dateEntries = dates.map(DateEntry); // Now, we can use momentjs for our date logic

将属于在一起的日期条目分组

现在我们有了日期列表,我们可以按月对它们进行分组。我实现了一个快速的groupBy帮助程序,您可以复制它,也可以使用下划线或ramda之类的库中的程序。

要按月分组,我们传递了一个每月返回唯一字符串的函数:

const entriesByMonth = groupBy(
  ({ date }) => date.format("MM.YYYY"),
  entries
);

将条目数组合并到一项中

现在,我们停留在同一日期的条目组。您希望最终得到一个简单的对象列表,并且想要在date标签中掌握它们的范围。

同样,我们可以定义一个辅助函数,将这些组转换为更易于使用的对象:

const EntryRange = ( dateEntries ) => {
  const dates = dateEntries.map(d => d.date);
  const data = dateEntries.map(d => d.data);

  const from = moment.min(dates);
  const to = moment.max(dates);

  return { dates, data, from, to };
};

const ranges = Object.values(entriesByMonth).map(EntryRange);

现在,我们有了一个简单的对象列表;每月条目一个对象。这些对象已经知道它们的开始和结束日期!

转换回所需的输出

剩下要做的就是编写一个函数,将EntryRange对象转换回您想要的格式:

ranges.map(({ from, to, data }) => ({
  date: `${from.format("DD.MM.YYYY")} - ${to.format("DD.MM.YYYY")}`,
  data: data[0]
}))

请注意,创建字符串可以利用momentjs的格式化方法!

将它们放在一起

这是可运行代码段中的代码。

// 1: Raw input data
const dates   = [
  { date: "26.06.2019", someData: "foo" },
  { date: "27.06.2019", someData: "foo" },
  { date: "28.06.2019", someData: "foo" },
  { date: "29.06.2019", someData: "foo" },
  { date: "30.06.2019", someData: "foo" },
  { date: "01.07.2019", someData: "foo" },
  { date: "02.07.2019", someData: "foo" },
  { date: "03.07.2019", someData: "foo" },
  { date: "04.07.2019", someData: "foo" },
  { date: "05.07.2019", someData: "foo" },
  { date: "06.07.2019", someData: "foo" },
  { date: "08.08.2019", someData: "foo" },
  { date: "01.01.2020", someData: "foo" },
];

// 2: Define models
const DateEntry = ({ date, someData }) => ({
  date: moment(date, "DD.MM.YYYY"),
  data: someData
});

const EntryRange = ( dateEntries ) => {
  const dates = dateEntries.map(d => d.date);
  const data = dateEntries.map(d => d.data);
  
  const from = moment.min(dates);
  const to = moment.max(dates);
  
  return {
    dates,
    data,
    from,
    to
  }
};

EntryRange.sorter = (r1, r2) => r1.from.isBefore(r2.from) ? -1 : 1;

// 3. Convert data to easy-to-work-with formats
const entries = dates.map(DateEntry);
const entriesByMonth = groupBy(
  ({ date }) => date.format("MM.YYYY"),
  entries
);
// Sorted list of EntryRanges
const entryGroups = Object
  .values(entriesByMonth)
  .map(EntryRange)
  .sort(EntryRange.sorter);


// 4. Convert back to desired output
console.log(
  entryGroups
    .map(({ from, to, data }) => ({
      date: `${from.format("DD.MM.YYYY")} - ${to.format("DD.MM.YYYY")}`,
      data: data[0]
    }))
)

// Utils
function groupBy(getKey, items) {
  return items.reduce(
    (groups, item) => {
      const k = getKey(item);
      if (!groups[k]) groups[k] = [ item ];
      else groups[k].push(item);
      return groups;
    }, {});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>

分解这些数据转换的好处

现在我们有一段代码可以简化每一步,我们可以通过编写一些小的更改来做很多事情。我将作两个更改,只是为了表明我们可以:)

  1. 将分组逻辑更改为仅按年份分组
  2. 支持通过,联接不同的数据条目

亲自尝试一下,看看如何获​​得不同的结果!

// 1: Raw input data
const dates   = [
  { date: "26.06.2019", someData: "foo" },
  { date: "27.06.2019", someData: "foo" },
  { date: "28.06.2019", someData: "foo" },
  { date: "29.06.2019", someData: "foo" },
  { date: "30.06.2019", someData: "bar" },
  { date: "01.07.2019", someData: "foo" },
  { date: "02.07.2019", someData: "foo" },
  { date: "03.07.2019", someData: "foo" },
  { date: "04.07.2019", someData: "foo" },
  { date: "05.07.2019", someData: "foo" },
  { date: "06.07.2019", someData: "foo" },
  { date: "08.08.2019", someData: "foo" },
  { date: "01.01.2020", someData: "foo" },
];

// 2: Define models
const DateEntry = ({ date, someData }) => ({
  date: moment(date, "DD.MM.YYYY"),
  data: someData
});

const EntryRange = ( dateEntries ) => {
  const dates = dateEntries.map(d => d.date);
  const data = dateEntries.map(d => d.data);
  
  const from = moment.min(dates);
  const to = moment.max(dates);
  
  return {
    dates,
    data,
    from,
    to
  }
};

EntryRange.sorter = (r1, r2) => r1.from.isBefore(r2.from) ? -1 : 1;

// 3. Convert data to easy-to-work-with formats
const entries = dates.map(DateEntry);
const entriesByYear = groupBy(
  ({ date }) => date.format("YYYY"),
  entries
);
// Sorted list of EntryRanges
const entryGroups = Object
  .values(entriesByYear)
  .map(EntryRange)
  .sort(EntryRange.sorter);


// 4. Convert back to desired output
console.log(
  entryGroups
    .map(({ from, to, data }) => ({
      date: `${from.format("DD.MM.YYYY")} - ${to.format("DD.MM.YYYY")}`,
      data: [...new Set(data)].join(", ")
    }))
)

// Utils
function groupBy(getKey, items) {
  return items.reduce(
    (groups, item) => {
      const k = getKey(item);
      if (!groups[k]) groups[k] = [ item ];
      else groups[k].push(item);
      return groups;
    }, {});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>

答案 1 :(得分:0)

您可以通过遍历当前数组并将第一个对象复制到该数组并将date属性更新为当前范围来生成新对象的新数组。然后,对于每个后续日期为同一月第二天的后续对象,将结果数组中最后一个对象的日期值作为最新日期。

否则,将具有更新的日期值的新对象添加到数组。如果日期顺序中断,则会在当月创建多个范围。尚不清楚这是否是您想要的。

例如下面使用几个简单的助手来分析字符串并添加一天。您可以根据需要将其替换为库调用。

不清楚要保留哪个“ someData”,以下保留第一个,但可以轻松保留最后一个。

let dates   = [
  { date: "26.06.2019", someData: "foo" },
  { date: "27.06.2019", someData: "foo" },
  { date: "28.06.2019", someData: "foo" },
  { date: "29.06.2019", someData: "foo" },
  { date: "30.06.2019", someData: "foo" },
  { date: "01.07.2019", someData: "foo" },
  { date: "02.07.2019", someData: "foo" },
  { date: "03.07.2019", someData: "foo" },
  { date: "04.07.2019", someData: "foo" },
  { date: "05.07.2019", someData: "foo" },
  { date: "06.07.2019", someData: "foo" },
  { date: "08.08.2019", someData: "foo" }
];

// Parse DD.MM.YYYY to Date. Seperator can be
// any non–digit character
function parseDMY(s) {
  let b = s.split(/\D/);
  return new Date(b[2], b[1]-1, b[0]);
}
// Return a new date that is the passed date + 1 day
function addDay(date) {
  let d = new Date(+date);
  d.setDate(d.getDate() + 1);
  return d;
}

let result = dates.reduce((acc, obj) => {
  // Copy the passed in object
  let temp = Object.assign({}, obj);
  // Get the last entry in accumulator, use a default object if first iteration
  let last = acc.length? acc[acc.length - 1] :  {date:' - '};
  // Get the current date as string
  let currentS = temp.date;
  // Get the current date as Date
  let currentD = parseDMY(currentS);
  // Get the previous end date as Date,
  let lastEndD = parseDMY(last.date.split(' ')[2]);
  // Get the next day as Date
  let lastEndNextD = addDay(lastEndD);

  // If current date is the day after last end date
  // and in the same month, update date range
  if (+currentD == +lastEndNextD &&
      currentD.getMonth() == lastEndD.getMonth()) {
    last.date = last.date.replace(/\S+$/, currentS)

  // Otherwise, start a new entry with an updated range
  } else {
    temp.date = temp.date + ' - ' + temp.date;
    acc.push(temp);
  }
  
  return acc;
}, []);

console.log(result);