【问题标题】:Convert Rows to Columns using RxJS / TypeScript / JavaScript使用 RxJS / TypeScript / JavaScript 将行转换为列
【发布时间】:2021-02-23 22:30:01
【问题描述】:

我有下表数据:

date value
01/01/2000 1
01/02/2000 2
01/01/2001 2
01/01/2002 1.5
01/02/2002 1.6
[{date: "01/01/2000", value: "1"},{date: "01/02/2000", value: "2"},{date: "01/01/2001", value: "2"},{date: "01/01/2002", value: "1.5"},{date: "01/02/2002", value: "1.6"}]

我想将其转换为列:

Year Jan Feb
2000 1 2
2001 2
2002 1.5 1.6
[{Year: "2000", Jan: "1", Feb: "2"},
{Year: "2001", Jan: "", Feb: "2"},
{Year: "2002", Jan: "1.5", Feb: "1.6"}]

如何在 Angular 中使用 RxJS / TypeScript / JavaScipt 更改它?

谢谢。

【问题讨论】:

  • 我想也许你在 2001 年的输出中调换了 Jan 和 Feb (jan = 2, feb = '')?

标签: javascript angular typescript rxjs


【解决方案1】:

见下面使用reduce的方法

const initial = [{date: "01/01/2000", value: "1"},{date: "01/02/2000", value: "2"},{date: "01/01/2001", value: "2"},{date: "01/01/2002", value: "1.5"},{date: "01/02/2002", value: "1.6"}]

const allMonths = initial.reduce((prev, next) => {
  const date = next.date.substr(6,4) + '/' + next.date.substr(3,2)
  const month =  new Date(date).toLocaleString('default', { month: 'short' });
  return {...prev, [month]: ''}
}, {})

const temp = initial.reduce(
  (prev, next) => {
    const date = next.date.substr(6,4) + '/' + next.date.substr(3,2)
    const month =  new Date(date).toLocaleString('default', { month: 'short' });
    
    const Year = new Date(date).getFullYear()
    let prevYearVal = prev[Year]
    if(!prevYearVal) { prevYearVal = {Year,...allMonths} ;}
    return {...prev, [Year]: {...prevYearVal,Year, [month]: next.value}}
    
    return prev
  },
  {}
)
const final = Object.values(temp)
console.log(final)

【讨论】:

  • 感谢@Owen,正是我想要的。
  • @developer - 是吗?这不是跨浏览器。它不适用于 MacOS 或 iOS 上的 Safari。认为你应该知道。
【解决方案2】:

这需要多个步骤来完成转换。

  1. 将字符串日期转换为可用的 JavaScript 日期对象
    • 因为日期是 mm/dd/YYYY,而不是 dd/mm/YYYY,所以它必须调换
    • 然后使用分解后的字符串构造一个新的日期
  2. 那么就是构造新对象的问题了
  3. 的输出是每个日期与必须组合的相应值
    • 如果Year 正确匹配,则第二个迭代器 (.reduce()) 会合并对象

const input = [{date: "01/01/2000", value: "1"},{date: "01/02/2000", value: "2"},{date: "01/01/2001", value: "2"},{date: "01/01/2002", value: "1.5"},{date: "01/02/2002", value: "1.6"}];

const result = input.map(i => {
  const [day, month, year] = i.date.split('/');
  const [_, monthName] = new Date(year, month - 1, day).toDateString().split(' ');
  return {
    Year: year,
    [monthName]: i.value
  }
}).reduce((acc, d, idx) => {
  if (idx == 0) { 
    acc.push(d);
  } else if (acc[acc.length - 1].Year == d.Year) {
    acc[acc.length - 1] = Object.assign(acc[acc.length-1], d);
  } else {
    acc.push(d);
  }
  return acc;
}, []);

console.log(result);

【讨论】:

  • OP 说它运行良好,我喜欢这个实现,但我注意到预期的第二个条目是{Year: '2001', Jan: 2, Feb: ''} 的遗漏。您的版本不包含空白 Feb 节点。
  • @ScottSauyet 我从一开始就意识到了这一点,但我不愿意“弥补”源中没有的数据。这只是一个不好的做法。如果 OP 想要冒险,那是 OP 的特权。 “一个有手表的人知道现在几点。一个有两个手表的人永远不确定。”——关于单一真理来源的西格尔定律。
  • 当然,在这个概念和我们应该返回一致的数据结构的想法之间存在一种平衡行为,而不是使用year/jan/feb 和另一个使用year/jan。显然,如果要将其显示在表格中,则有时必须创建丢失的数据,即使它只是显示代码中某处的... | ''。这是否是正确的地方,这是一个很好的问题。但你知道这一点并做出了合理的选择,这很好。只是不清楚你是否做出了明确的决定。
【解决方案3】:

这是一个与 Owen 和 Randy 的答案类似的版本,但它将月份名称处理分离到自己的辅助函数中。独立编写,局部变量名各不相同,但做的工作是一样的。它也被构造为单个函数调用而不是一组步骤:

const monthName = ((months) => (m) => months [m - 1])(
  '01|02|03|04|05|06|07|08|09|10|11|12' .split ('|') .map (
    m => new Date (`2021/${m}`).toLocaleString('default', {month: 'short'})
  )
)

const transform = (xs) => {
  const base = Object .fromEntries (
    [...new Set(input .map (
      ({date}) => date .slice (3, 5)
    ))]
    .map (month => [monthName (month), ""])
  )
  return Object .values (xs .reduce ((years, {date, value}) => {
    const Year = date .slice (6, 10), 
          Month = date.slice (3, 5)
    years [Year] = years [Year] || {Year, ...base}
    years [Year] [monthName(Month)] = value
    return years
  }, {}))
}

const input = [{date: "01/01/2000", value: "1"}, {date: "01/02/2000", value: "2"}, {date: "01/01/2001", value: "2"}, {date: "01/01/2002", value: "1.5"}, {date: "01/02/2002", value: "1.6"}]

console .log (transform (input))
.as-console-wrapper {max-height: 100% !important; top: 0}

一个优点是它不会为每个对象调用Date 构造函数,只需在每个日历月调用一次即可。如果您不想要语言环境字符串版本,而是一组固定的月份名称,则帮助程序可以更简单:

const monthName = ((months) => (m) => months [m - 1]) (
  'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec' .split ('|')
)

我更喜欢纯粹作为表达式而不是语句编写的函数,因此替代版本可能如下所示:

const transform = (
  xs, 
  base = Object .fromEntries (
    [...new Set (xs .map (
      ({date}) => date .slice (3, 5)
    ))]
    .map (month => [monthName (month), ""])
  )
) => Object .values (
  xs .reduce ((a, {date, value}, _, __, 
    Year = date .slice (6, 10), Month = date.slice (3, 5)
  ) => ({
    ...a, 
    [Year]: {Year, ...(a [Year] || base), [monthName (Month)]: value}
  }), {})
)

它的工作方式相同,有点less efficient,但我觉得它更干净。

【讨论】:

  • 感谢 Scott,这确实是一个更清晰的实现。
猜你喜欢
  • 2016-11-18
  • 2023-02-05
  • 1970-01-01
  • 1970-01-01
  • 2021-05-04
  • 2013-04-18
  • 1970-01-01
  • 2015-04-25
  • 2017-03-07
相关资源
最近更新 更多