I'm building a news feed using React and moment.js. Using .map
I'm rendering items with a title and content. I'd like to check if an item was posted in the same year and month as another item. If this is the case I want to hide the second items title.
Please see my fiddle
Currently my code renders this:
news item one
news item two
news item three
news item four
Since item one and two share the same month and year I would like to render like this instead:
news item one
news item two
news item three
news item four
Based on this answer I've tried to find nodes with duplicate class names but I'm not quite there:
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' ]
Maybe I'm going about this the wrong way and using .map
in the first place is a bad idea?
Update
Thanks for all the great answers, it was hard to pick between them as they all work well. I have to accept David Ibl's answer since he was first to provide a working example.
You can use the includes() method in JavaScript to check if an item exists in an array. You can also use it to check if a substring exists within a string. It returns true if the item is found in the array/string and false if the item doesn't exist.
To check if all values in an array are equal:Use the Array. every() method to iterate over the array. Check if each array element is equal to the first one. The every method only returns true if the condition is met for all array elements.
To use a condition inside map() in React:Call the map() method on an array. Use a ternary operator to check if the condition is truthy. The operator returns the value to the left of the colon if the condition is truthy, otherwise the value to the right is returned.
Array.prototype.reduce()
can be leveraged to organise your Articles
in Object
form by year
and month
.
See below for a practical example.
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>
you can do it this way: 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>
)
}
Maybe you should sort items based on year and month before to ensure correct behaviour even when items are not sorted initially. But this works with your example.
Assuming the data structure looked like this:
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'
}
}
]
You could use Array.reduce:
const formatted = data.reduce((obj, item) => {
obj[item.date] = obj[item.date] ? [...obj[item.date], item] : [item]
return obj
}, {})
Example Codepen
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With