Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking if items within mapped array share values

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:

March 2018

news item one

March 2018

news item two

September 2017

news item three

June 2017

news item four

Since item one and two share the same month and year I would like to render like this instead:

March 2018

news item one

news item two

September 2017

news item three

June 2017

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.

like image 207
Pixelomo Avatar asked Mar 14 '18 08:03

Pixelomo


People also ask

How do you check if an element is in array in another array?

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.

How do you check if an array has the same values?

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.

How do you add a condition to an array map?

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.


3 Answers

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>
like image 167
Arman Charan Avatar answered Oct 18 '22 03:10

Arman Charan


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.

like image 25
David Ibl Avatar answered Oct 18 '22 02:10

David Ibl


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

like image 1
Mike Avatar answered Oct 18 '22 02:10

Mike