Как получить новый массив только с уникальными идентификаторами во время фильтрации и сопоставления в Js?

#javascript #arrays #ecmascript-6 #javascript-objects #ecmascript-5

#javascript #массивы #ecmascript-6 #javascript-объекты #ecmascript-5

Вопрос:

Я пытаюсь получить элементы dropdwon из тегов и ключевых слов. Я попытался привести их к одному и тому же формату для каждой из них, фильтруя и отображая следующим образом:

   const productsWithTags = products
          .filter(product => !!product.tagId)
          .map(item => ({
            id: item.tagId,
            name: item.tagName,
          }))
  

Дело в том, что он добавляет дубликаты, поскольку некоторые продукты имеют одинаковые идентификаторы тегов, как я могу сохранить этот массив уникальным?

Ответ №1:

Сначала вам нужно выбрать отдельные элементы по идентификатору тега, а затем сопоставить результат

 const products= [{tagId:1,tagName:"Tag 1"}, {tagId:2,tagName:"Tag 2"}, {tagId:3,tagName:"Tag 3"}, {tagId:1,tagName:"Tag 1-2"}, {tagId:3,tagName:"Tag 3-2"}, {tagId:2,tagName:"Tag 2-2"}]
const productsWithTags = products
          .filter(function(value,index,self){return self.indexOf(self.find(item =>item.tagId==value.tagId)) === index})
          .map(item => ({
            id: item.tagId,
            name: item.tagName,
          }))
console.log(productsWithTags)  

Комментарии:

1. Вы также можете использовать findIndex напрямую вместо insteadOf find

Ответ №2:

Вместо tagId того, чтобы приводить свойство к a Boolean , вы должны создать таблицу поиска, которая сообщает вам, был ли tagId элемент уже просмотрен / посещен, а затем фильтровать на основе этого результата.

Ниже вы можете увидеть рабочий пример с аннотациями.

 const products = [
  {
    tagId: 'a',
    tagName: 'Tag A'
  },
  {
    tagId: 'b',
    tagName: 'Tag B'
  },
  {
    tagId: 'a',
    tagName: 'Tag A'
  },
  {
    tagId: 'c',
    tagName: 'Tag C'
  }
];

function uniqueBy(property) {
  let seen = Object.create(null); // closure, tells us the already seen id's
  return function (item) {        // predicate function
    let key = item[property];     // get the "key" value (here: tagId)
    if (seen[key] == null) {      // is the key not inside the seen closure?
      seen[key] = 1;              // then add it and
      return true;                // accept the item
    }
    return false;                 // otherwise reject the item
  };
}

const result = products.
  filter(uniqueBy('tagId')).      // use "uniqueBy" to create a predicate
  map(product => {
    return {
      id: product.tagId,
      name: product.tagName
    };
  });
  
console.log(result);  

Подсказка: вы можете сделать это более производительным, избегая одного шага итерации. Как? Используйте .reduce вместо .filter и .map .

 const products = [
  {
    tagId: 'a',
    tagName: 'Tag A'
  },
  {
    tagId: 'b',
    tagName: 'Tag B'
  },
  {
    tagId: 'a',
    tagName: 'Tag A'
  },
  {
    tagId: 'c',
    tagName: 'Tag C'
  }
];

function uniqueBy(property) {
  let seen = Object.create(null); // closure, tells us the already seen id's
  return function (item) {        // predicate function
    let key = item[property];     // get the "key" value (here: tagId)
    if (seen[key] == null) {      // is the key not inside the seen closure?
      seen[key] = 1;              // then add it and
      return true;                // accept the item
    }
    return false;                 // otherwise reject the item
  };
}


function filterMap(filterer, mapper) {
  return function (accumulator, item) {
    return filterer(item)
      ? [...accumulator, mapper(item)]
      : accumulator;
  }
}

const result = products.
  reduce(
    filterMap(
      uniqueBy('tagId'),
      product => {
          return {
            id: product.tagId,
            name: product.tagName
          };
      }
    ),
    []
  );
  
console.log(result);