Динамически получать значения объекта из массива

Допустим, у меня есть объект myBook и массив allCategories.

const allCategories = ["sciencefiction", "manga", "school", "art"];

const myBook = {
   isItScienceFiction: true,
   isItManga: false,
   isItForKids: false
}

Что я хочу : Перебрать категории, чтобы проверить значение книги, например, проверить, существует ли "sciencefiction" в моем объекте книги, а затем проверить его значение

Что я пробовал:

1) С indexOf

allCategories.map((category) => {
    Object.keys(myBook).indexOf(category) 
    // Always returns -1 because "sciencefiction" doesn't match with "isItScienceFiction"
});

2) С includes

allCategories.map((category) => {
    Object.keys(myBook).includes(category) 
    // Always returns false because "sciencefiction" doesn't match with "isItScienceFiction"
});

Ожидаемый результат:

allCategories.map((category) => {
   // Example 1 : Returns "sciencefiction" because "isItScienceFiction: true"
   // Example 2 : Returns nothing because "isItManga: false"
   // Example 3 : Returns nothing because there is not property in myBook with the word "school"
   // Example 4 : Returns nothing because there is not property in myBook with the word "art"


   // If category match with myBook categories and the value is true then
    return (
         <p>{category}</p>
    );
});

Если вам нужна дополнительная информация, просто дайте мне знать, я отредактирую свой вопрос.

🤔 А знаете ли вы, что...
JavaScript имеет множество библиотек и фреймворков, таких как jQuery, Angular, и Vue.js.


79
8

Ответы:

Решено

Вы можете использовать методы filter и find для возврата нового массива категорий, а затем использовать метод map для возврата массива элементов.

const allCategories = ["sciencefiction", "manga", "school", "art"];
const myBook = {isItScienceFiction: true, isItManga: false, isItForKids: false}

const result = allCategories.filter(cat => {
  const key = Object.keys(myBook).find(k => k.slice(4).toLowerCase() === cat);
  return myBook[key]
}).map(cat => `<p>${cat}</p>`)

console.info(result)

Вы также можете использовать reduce вместо filter и map и endsWith метода.

const allCategories = ["sciencefiction", "manga", "school", "art"];
const myBook = {isItScienceFiction: true,isItManga: false,isItForKids: false}

const result = allCategories.reduce((r, cat) => {
  const key = Object.keys(myBook).find(k => k.toLowerCase().endsWith(cat));
  if (myBook[key]) r.push(`<p>${cat}</p>`)
  return r;
}, [])

console.info(result)

Вы можете использовать

Object.keys(myBook).forEach(function(key){console.info(myBook[key])})

... поместите свой код вместо console.info. Это может помочь без жесткого кодирования, а также является передовой практикой.


Вы действительно не должны хранить ряд свойств, содержащих логические значения. Хотя это может сработать для 1, 2 или 3 категорий, для нескольких сотен это не сработает. Вместо этого просто сохраните категории в массиве:

 const myBook = {
   categories: ["sciencefiction", "manga", "kids"],
 };

Если у вас уже есть объект со старой структурой, вы можете легко преобразовать его:

 const format = old => {
  const categories = [];

  if (old.isItScienceFiction)
    categories.push("sciencefiction");
  if (old.isItManga)
     categories.push("manga");
  if (old.isItForKids)
     categories.push("kids");

   return { categories };
 };

Теперь, чтобы проверить, содержит ли книга определенную категорию:

  const isManga = myBook.categories.includes("manga");

И ваш рендеринг теперь также довольно прост:

 myBook.categories.map(it => <p>{it}</p>)

Вы можете создать Map для категорий и ключей объекта:

const allCategories = ["sciencefiction", "manga", "school", "art"],
      myBook = { isItScienceFiction:true, isItManga:false, isItForKids:false }

const map = Object.keys(myBook)
                   .reduce((r, k) => r.set(k.slice(4).toLowerCase(), k), new Map);

/* map:
   {"sciencefiction" => "isItScienceFiction"}
   {"manga" => "isItManga"}
   {"forkids" => "isItForKids"}
*/

allCategories.forEach(key => {
  let keyInObject = map.get(key); // the key name in object
  let value = myBook[keyInObject]; // value for the key in object
  
  console.info(key, keyInObject, value)

  if (keyInObject && value) {
    // do something if has the current key and the value is true
  }
})

Используйте Array.filter() и Array.find() с регулярным выражением, чтобы найти категории с совпадающими ключами. Используйте Array.map() для преобразования категорий в строки/JSX/и т.д....

const findMatchingCategories = (obj, categories) => {
  const keys = Object.keys(obj);
  
  return allCategories
    .filter(category => {
      const pattern = new RegExp(category, 'i');
      
      return obj[keys.find(c => pattern.test(c))];
    })
    .map(category => `<p>${category}</p>`);
};

const allCategories = ["sciencefiction", "manga", "school", "art"];

const myBook = {
   isItScienceFiction: true,
   isItManga: false,
   isItForKids: false
};

const result = findMatchingCategories(myBook, allCategories);
  
console.info(result);

Вы можете попробовать так:

const allCategories = ["sciencefiction", "manga", "school", "art"];

const myBook = {
  isItScienceFiction: true,
  isItManga: false,
  isItForKids: false
};

const myBookKeys = Object.keys(myBook);

const result = allCategories.map(category => {
  const foundIndex = myBookKeys.findIndex(y => y.toLowerCase().includes(category.toLowerCase()));
  
  if (foundIndex > -1 && myBook[myBookKeys[foundIndex]])
    return `<p>${category}</p>`;
});

console.info(result);

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

const allCategories = ["sciencefiction", "manga", "school", "art"];

const myBook = {
  isItScienceFiction: true,
  isItManga: false,
  isItForKids: false
}

const modBook = {}

Object.keys(myBook).map((key) => {
  const modKey = key.slice(4).toLowerCase()
  modBook[modKey] = myBook[key]
})

const haveCategories = allCategories.map((category) => {
  if (modBook[category]) {
    return <p>{category}</p>
  }
  return null
})
console.info(haveCategories)

Преобразование sciencefiction в isItScienceFiction невозможно, а зацикливание всех ключей myBook для каждого category не оптимально.

Но преобразовать isItScienceFiction в sciencefiction довольно просто, поэтому вы можете создать newMyBook из своего myBook и использовать его для проверки.

Создание newMyBook — это одноразовые накладные расходы.

const allCategories = ["sciencefiction", "manga", "school", "art"];
const myBook = {isItScienceFiction: true,isItManga: false,isItForKids: false};

const newMyBook = Object.keys(myBook).reduce((a, k) => {
    return { ...a, [k.replace('isIt', '').toLowerCase()]: myBook[k] };
}, {});

console.info(
    allCategories.filter(category => !!newMyBook[category]).map(category => `<p>${category}</p>`)
);