检查映射数组中的项是否共享值

时间:2018-03-14 08:04:39

标签: javascript arrays reactjs time duplicates

我正在使用React和moment.js构建新闻Feed。使用.map我正在渲染带有标题和内容的项目。我想检查一件商品是否在同一年份和另一件商品中发布。如果是这种情况,我想隐藏第二项标题。

Please see my fiddle

目前我的代码呈现了这个:

2018年3月

新闻第一项

2018年3月

新闻第二项

2017年9月

新闻第三项

2017年6月

新闻第四项

  

由于第一项和第二项共享同一个月和一年,我想这样做:

2018年3月

新闻第一项

新闻第二项

2017年9月

新闻第三项

2017年6月

新闻第四项

Based on this answer我试图找到具有重复类名的节点,但我不在那里:

let monthNodes = [...document.querySelectorAll('.months')];
let months = []

monthNodes.map(month => {
 months.push(month.className) 
})

const count = names => 
names.reduce((a, b) => 
Object.assign(a, {[b]: (a[b] || 0) + 1}), {})

const duplicates = dict => 
Object.keys(dict).filter((a) => dict[a] > 1)

console.log(count(months)) // {March_2018: 2, December_2017: 1, November_2017: 1}
console.log(duplicates(count(months))) // [ 'March_2018' ]

也许我会以错误的方式解决这个问题并且首先使用.map是一个坏主意?

更新

感谢所有出色的答案,很难在他们之间挑选,因为他们都运作良好。自从他第一次提供一个有效的例子以来,我必须接受David Ibl的回答。

5 个答案:

答案 0 :(得分:2)

可以利用

Array.prototype.reduce()ArticlesObject之后以year格式整理month

请参阅下面的实例。

JSFiddle

// News
class News extends React.Component {

  // State.
  state = {  
    news: [ 
        {content: 'news item one -- march 2018', date: '2018-03-02'},
        {content: 'news item two -- march 2018', date: '2018-03-17'},
        {content: 'news item two -- sep 2017', date: '2017-09-21'},
        {content: 'news item one -- june 2017', date: '2017-06-15'}
    ]
  }
  
  // Render.
  render = () => (
    <div>
      {
        Object.entries(this.organised()).map(([yearmonth, articles], i) => (
          <div key={i}>
            <h3>{yearmonth}</h3>
            {articles.map((article, j) => (
              <div key={j}>
                {article.content}
              </div>
            ))}
          </div>
        ))
      }
    </div>
  )
  
  // Organised.
  organised = () => this.state.news.reduce((total, article) => {
    const key = moment(article.date).format('YYYY MMMM')
    return {
      ...total,
      [key]: total[key] && total[key].concat(article) || [article]
    }
  }, {})
}

// Mount.
ReactDOM.render(<News/>,document.getElementById('container'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.21.0/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container"></div>

答案 1 :(得分:1)

你可以这样做: https://jsfiddle.net/2e27jvvh/

render() {
const arr = new Array();
return( 
    <div>
    {this.state.news.map(item => {
        const yearMonth = moment(item.date).format("MMMM YYYY");
        const yearM = moment(item.date).format("MMMM_YYYY");
        if (arr.indexOf(yearMonth) < 0){
            arr.push(yearMonth);
          return (
          <div className={'months ' + yearM}>
              <h2>{yearMonth}</h2>
              <p>{item.content}</p> 
          </div>
            ) 
        } else {
            return (
          <div className={'months ' + yearM}>
              <p>{item.content}</p> 
          </div>
            ) 
        }

        }) 
    }
 </div>
)
}

也许您应该根据年份和月份对项目进行排序,以确保即使最初未对项目进行排序也能保证正确的行为。但这适用于你的例子。

答案 2 :(得分:1)

假设数据结构如下所示:

const data = [
  {
    date: 'March 2018',
    item: {
        title: 'news item one'
    }
  },
  {
    date: 'March 2018',
    item: {
      title: 'news item two'
    }
  },
  {
    date: 'September 2017',
    item: {
      title: 'news item two'
    }
  },
  {
    date: 'June 2017',
    item: {
      title: 'news item one'
    }
  }
]

您可以使用https://codepen.io/hulvire/pen/KoVrZb

const formatted = data.reduce((obj, item) => {
  obj[item.date] = obj[item.date] ? [...obj[item.date], item] : [item]

  return obj
}, {})

Array.reduce

答案 3 :(得分:1)

1)您可以定义previousYearMonth变量并在那里存储上一年和月份名称。您可以检查当前的yearMonthpreviousYearMonth是否相同并显示/隐藏相应的标记:

render() {
  return(
    <div>
      {this.state.news.map((item, index) => {

        const yearMonth = moment(item.date).format("MMMM YYYY");
        const yearM = moment(item.date).format("MMMM_YYYY");
        const previousYearMonth = index
          ? moment(this.state.news[index - 1].date).format("MMMM YYYY")
          : false;

        return(
          <div className={'months ' + yearM}>
            {
              (yearMonth !== previousYearMonth) && (<h2>{yearMonth}</h2>)
            }
            <p>{item.content}</p>
          </div>
        )
      })
      }
    </div>
  )
}

检查the fork of your fiddle

2)您可以使用constructor方法预处理数据并以这种方式设置title属性的另一种方法:

constructor(props) {
  super(props);
  const data = [
    {content: 'news item one', date: '2018-03-02'},
    {content: 'news item two', date: '2018-03-17'},
    {content: 'news item two', date: '2017-09-21'},
    {content: 'news item one', date: '2017-06-15'}
  ];

  data.forEach((item, index) => {
    const yearMonth = moment(item.date).format("MMMM YYYY");
    const previousYearMonth = index
      ? moment(data[index - 1].date).format("MMMM YYYY")
      : false;

    if (yearMonth !== previousYearMonth) {
      item.title = yearMonth
    }
  });

  this.state = {
    news: data
  }
}

在这种情况下,在render方法中,您只需检查title属性是否存在并显示标题:

<div>
  {
    item.title && (<h2>{item.title}</h2>)
  }
  <p>{item.content}</p>
</div> 

检查the fiddle with this approach

答案 4 :(得分:1)

考虑到此列表来自API,一旦数据到达,您就可以拥有类似的内容。

const monthwise = this.state.news.reduce((res, obj) => {
  const ym = moment(obj.date).format("YYYY-MM")
  res[ym] = res[ym] || []
  res[ym].push(obj);
  return res;
}, {})

this.setState({monthwise})

现在,在您的render中,您可以拥有:

render() {
  return( 
    <div>
      {Object.values(this.state.monthwise).map((items, index) => {
        return(
          <div key={index}>
            <h4>{moment(items[0].date).format("YYYY MMMM")}</h4>  
            {items.map((item, key) => <div key={key}>{item.content}</div>   )}
          </div>
        ) 
      })}
    </div>
  )
}

Working jsfiddle