检查映射数组中的项目是不是共享值
Posted
技术标签:
【中文标题】检查映射数组中的项目是不是共享值【英文标题】:Checking if items within mapped array share values检查映射数组中的项目是否共享值 【发布时间】:2018-08-22 16:05:23 【问题描述】:我正在使用 React 和 moment.js 构建新闻提要。使用.map
我正在渲染带有标题和内容的项目。我想检查一个项目是否与另一个项目在同一年和同一月发布。如果是这种情况,我想隐藏第二个项目的标题。
Please see my fiddle
目前我的代码呈现这个:
2018 年 3 月
新闻一
2018 年 3 月
新闻二
2017 年 9 月
新闻三
2017 年 6 月
新闻四
由于项目 1 和 2 共享相同的月份和年份,我想改为这样呈现:
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 的回答,因为他首先提供了一个工作示例。
【问题讨论】:
【参考方案1】:Array.prototype.reduce()
可以通过year
和month
以Object
形式组织您的Articles
。
请参阅下面的实际示例。
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>
【讨论】:
如果我能接受多个答案,我会接受这个【参考方案2】:你可以这样做: 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>
)
也许您应该根据之前的年份和月份对项目进行排序,以确保即使最初未对项目进行排序也能保持正确的行为。但这适用于您的示例。
【讨论】:
【参考方案3】:假设数据结构如下所示:
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'
]
你可以使用Array.reduce:
const formatted = data.reduce((obj, item) =>
obj[item.date] = obj[item.date] ? [...obj[item.date], item] : [item]
return obj
, )
Example Codepen
【讨论】:
遗憾的是,请参阅我的小提琴以了解数据的结构 这是从 api 获取的【参考方案4】:1) 您可以定义previousYearMonth
变量并将之前的年份和月份名称存储在那里。您可以检查您当前的yearMonth
和previousYearMonth
是否相同并显示/隐藏相应的标签:
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。
【讨论】:
如果有多个重复项,这会起作用吗? "如果有多个重复项,这会起作用吗?"是的,检查这个小提琴 - jsfiddle.net/starw38a/1 如果我能接受多个答案,我会接受这个【参考方案5】:考虑到此列表来自 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
【讨论】:
如果我能接受多个答案,我会接受这个以上是关于检查映射数组中的项目是不是共享值的主要内容,如果未能解决你的问题,请参考以下文章