Home > Blockchain >  Filtering objects based on the value of an array of objects
Filtering objects based on the value of an array of objects

Time:05-12

I'm making a note-taking app in React and I have some data that looks like this. I'm wanting to filter it so that only the objects which contain a tag in an array remain, and the rest are removed.

Raw Data

const obj = {
  Mon: [
    { id: 1, content: 'Some text', tag: 'home' },
    { id: 2, content: 'Some text', tag: 'work' },
    { id: 3, content: 'Some text', tag: 'project' },
  ],
  Tue: [
    { id: 4, content: 'Some text', tag: 'project' },
    { id: 5, content: 'Some text', tag: 'moving' },
  ],
  Wed: [
    { id: 6, content: 'Some text', tag: 'home' },
    { id: 7, content: 'Some text', tag: 'home' },
    { id: 8, content: 'Some text', tag: 'work' },
  ],
};

Desired data after filtering "home" and "work"

Array to use as filtering terms

const filterTags = ['home', 'work']

Data we are left with

{
  Mon: [
    { id: 1, content: 'Some text', tag: 'home' },
    { id: 2, content: 'Some text', tag: 'work' },
  ],
  Wed: [
    { id: 6, content: 'Some text', tag: 'home' },
    { id: 7, content: 'Some text', tag: 'home' },
    { id: 8, content: 'Some text', tag: 'work' },
  ],
};

The reason for wanting to filter using an array is because I want a user to be able to click on the tags for the notes they want to see (these tags are currently stored in a useState()).

With the remaining data after filtering, I plan to map through it and render the relevant elements like this:

<>
  {Object.entries(sortedNotesData).map(
    ([noteDate, noteContent], i) => (
      <div key={i}>
        <NoteDate noteDate={noteDate} />
        <div className="column">
            {noteContent
              .map((note) => (
                <>
                  <NoteCard
                    key={note.id}
                    id={note.id}
                    content={note.content}
                    tag={note.tag}
                  />
                </>
              ))}
        </div>
      </div>
    )
  )}
</>

Any suggestions on a best practice way of filtering the raw data would be brilliant, including whether it would be best to handle the data filtering in a function outside of render(), or whether it can be done inline just before the .map().

CodePudding user response:

  • Convert the object to an array using Object.entries.

  • Map over the nested array and filter the values using the filterTags array.

  • Remove days that have no matching items in them.

  • Finally convert the nested array back to an object using Object.fromEntries

const obj = {
    Mon: [
      { id: 1, content: "Some text", tag: "home" },
      { id: 2, content: "Some text", tag: "work" },
      { id: 3, content: "Some text", tag: "project" },
    ],
    Tue: [
      { id: 4, content: "Some text", tag: "project" },
      { id: 5, content: "Some text", tag: "moving" },
    ],
    Wed: [
      { id: 6, content: "Some text", tag: "home" },
      { id: 7, content: "Some text", tag: "home" },
      { id: 8, content: "Some text", tag: "work" },
    ],
  },
  filterTags = ["home", "work"],
  filteredObj = Object.fromEntries(
    Object.entries(obj)
      .map(([key, value]) => [
        key,
        value.filter(({ tag }) => filterTags.includes(tag)),
      ])
      .filter(([, value]) => value.length)
  );

console.log(filteredObj);

You can also keep the days that have no matching items by simply removing the last filter.

const obj = {
    Mon: [
      { id: 1, content: "Some text", tag: "home" },
      { id: 2, content: "Some text", tag: "work" },
      { id: 3, content: "Some text", tag: "project" },
    ],
    Tue: [
      { id: 4, content: "Some text", tag: "project" },
      { id: 5, content: "Some text", tag: "moving" },
    ],
    Wed: [
      { id: 6, content: "Some text", tag: "home" },
      { id: 7, content: "Some text", tag: "home" },
      { id: 8, content: "Some text", tag: "work" },
    ],
  },
  filterTags = ["home", "work"],
  filteredObj = Object.fromEntries(
    Object.entries(obj).map(([key, value]) => [
      key,
      value.filter(({ tag }) => filterTags.includes(tag)),
    ])
  );

console.log(filteredObj);

CodePudding user response:

Similar to SSM's answer, but if you do not wish to include days with no results

  • Convert the object to an array using Object.entries.
  • filter the content using includes on the filterTags
  • only add the day with result of the filter returns 1 or more results

.

   const obj = {
      Mon: [
        { id: 1, content: 'Some text', tag: 'home' },
        { id: 2, content: 'Some text', tag: 'work' },
        { id: 3, content: 'Some text', tag: 'project' },
      ],
      Tue: [
        { id: 4, content: 'Some text', tag: 'project' },
        { id: 5, content: 'Some text', tag: 'moving' },
      ],
      Wed: [
        { id: 6, content: 'Some text', tag: 'home' },
        { id: 7, content: 'Some text', tag: 'home' },
        { id: 8, content: 'Some text', tag: 'work' },
      ],
    };
    
    const filterTags = ['home', 'work']
    //this will hold an object of results
    let filteredResults = {};
    
    Object.entries(obj).forEach(day => {
        const name = day[0];
        const filtered = day[1].filter(content => filterTags.includes(content.tag))
        if (filtered.length > 0) {
          filteredResults[name] = filtered
        }
    })
    
    console.log(filteredResults)

  • Related