Проверка общих значений элементов в сопоставленном массиве

Я создаю новостную ленту, используя React и moment.js. Используя .map, я визуализирую элементы с заголовком и содержанием. Я хотел бы проверить, был ли элемент размещен в том же году и месяце, что и другой элемент. В этом случае я хочу скрыть заголовок второго элемента.

Пожалуйста, посмотри на мою скрипку

В настоящее время мой код отображает это:

Март 2018 г.

новость первая

Март 2018 г.

новость вторая

Сентябрь 2017 г.

новость третья

Июнь 2017 г.

новость четвертая

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

Март 2018 г.

новость первая

новость вторая

Сентябрь 2017 г.

новость третья

Июнь 2017 г.

новость четвертая

На основании этого ответа Я пытался найти узлы с повторяющимися именами классов, но я не совсем там:

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.info(count(months)) // {March_2018: 2, December_2017: 1, November_2017: 1}
console.info(duplicates(count(months))) // [ 'March_2018' ]

Может, я ошибаюсь и использование .map в первую очередь - плохая идея?

Обновлять

Спасибо за все отличные ответы, было трудно выбирать между ними, так как все они работают хорошо. Я должен принять ответ Дэвида Ибла, поскольку он первым представил рабочий пример.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
7
0
95
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

вы можете сделать это так: 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>
)
}

Возможно, вам следует отсортировать элементы по году и месяцу раньше, чтобы обеспечить правильное поведение, даже если элементы не отсортированы изначально. Но это работает с вашим примером.

Предполагая, что структура данных выглядела так:

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
}, {})

Пример Codepen

к сожалению, нет, пожалуйста, посмотрите мою скрипку, чтобы узнать, как структурированы данные

Pixelomo 14.03.2018 09:38

это загружается из API

Pixelomo 14.03.2018 09:39

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>
  )
}

Отметьте вилка твоей скрипки.

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> 

Проверьте рабочий пример с этим подходом.

будет ли это работать, если существует более одного набора дубликатов?

Pixelomo 14.03.2018 09:45

"Будет ли это работать, если существует более одного набора дубликатов?" Да, проверьте эту скрипку - jsfiddle.net/starw38a/1

Mikhail Shabrikov 14.03.2018 09:47

если бы я мог принять более одного ответа, я бы принял это

Pixelomo 14.03.2018 10:02

Array.prototype.reduce() можно использовать для организации вашего Articles в форме Object с помощью 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>

если бы я мог принять более одного ответа, я бы принял это

Pixelomo 14.03.2018 10:02

Учитывая, что этот список поступает из 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>
  )
}

Рабочий jsfiddle

если бы я мог принять более одного ответа, я бы принял это

Pixelomo 14.03.2018 10:03

Другие вопросы по теме