BigBros
BigBros

Reputation: 83

Return an array of all matching elements Javascript/React

I have two arrays authors and posts. I want to return the posts arrays with the authors names not their emails, as seen below.

const authors = [
  {name: 'Thompson Smith', email: '[email protected]'},
  {name: 'John Doe', email: '[email protected]'},
  {name: 'Jane Coker', email: '[email protected]'},
  {name: 'Mirabel Ekong', email: '[email protected]'},
  {name: 'Samuel Doe', email: '[email protected]'},
  {name: 'Moses Philips', email: '[email protected]'},
  {name: 'Marcus Bowa', email: '[email protected]'},
  {name: 'Peter Touch', email: '[email protected]'},
  {name: 'Benson Bruce', email: '[email protected]'},
]

const posts = [
  { title: 'title one', authors: ['[email protected]', '[email protected]', '[email protected]'] },
  { title: 'title two', authors: ['[email protected]', '[email protected]', '[email protected]'] },
  { title: 'title three', authors: ['[email protected]', '[email protected]', '[email protected]'] },
  { title: 'title four', authors: ['[email protected]', '[email protected]', '[email protected]'] },
]

I want to return the posts with the actual name of the authors like this

 <div>
  <h2>{post.title}</h2>
  <p>
    <span>{post.author.name}</span>
    <span>{post.author.name}</span>
    <span>{post.author.name}</span>
  </p>
</div>

Please how can I achieve this in react/javascript?

EDIT: I forgot to add some really important parts of the question.

  1. In the posts array, there are some that have the actual names of the authors (not emails) and these names does not occur in the authors array, for example;

     { title: 'title one', authors: ['Michael Johnson', '[email protected]', '[email protected]'] }
    

In this case, I also want to retrieve the name Michael Johnson and retrieve the names of the rest of the authors from the authors array.

  1. In the authors array, there are extra props that I want to retrieve, such as the userIdand avatar. In essence the code looks like this;

   const authors = [
  {name: 'Thompson Smith', email: '[email protected]', userId: '001', avatar: '/avatar/1'},
  {name: 'John Doe', email: '[email protected]', userId: '002', avatar: '/avatar/2'},
  {name: 'Jane Coker', email: '[email protected]', userId: '003', avatar: '/avatar/3'},
  {name: 'Mirabel Ekong', email: '[email protected]', userId: '004', avatar: '/avatar/4'},
  {name: 'Samuel Doe', email: '[email protected]', userId: '005', avatar: '/avatar/5'},
  {name: 'Moses Philips', email: '[email protected]', userId: '006', avatar: '/avatar/6'},
  {name: 'Marcus Bowa', email: '[email protected]', userId: '007', avatar: '/avatar/7'},
  {name: 'Peter Touch', email: '[email protected]', userId: '008', avatar: '/avatar/8'},
  {name: 'Benson Bruce', email: '[email protected]', userId: '009', avatar: '/avatar'}
]

const posts = [
  { title: 'title one', authors: ['[email protected]', '[email protected]', '[email protected]'] },
  { title: 'title two', authors: ['[email protected]', '[email protected]', '[email protected]'] },
  { title: 'title three', authors: ['[email protected]', '[email protected]', '[email protected]'] },
  { title: 'title four', authors: ['[email protected]', '[email protected]', '[email protected]'] },
  { title: 'title five', authors: ['michael Johnson', '[email protected]', '[email protected]'] },
  { title: 'title six', authors: ['michael Johnson', 'Jane Joshua', '[email protected]'] },
]

EXPECTED OUTPUT

<div>
  <h2>{post.title}</h2>
  <ul>
    <li><Link to={userId}><Avatar src={avatar}/>{post.author.name}</Link></li>
    <li><Link to={userId}><Avatar src={avatar}/>{post.author.name}</Link></li>
    <li><Link to={userId}><Avatar src={avatar}/>{post.author.name}</Link></li>

//If the author does not exist in the authors array, should return
 <li><placeHolderAvatar />{post.author.name}</li>
  </ul>
</div>

Please how do I retrieve these extra props?

Upvotes: 0

Views: 2381

Answers (5)

Simon Rook
Simon Rook

Reputation: 205

One way is to map the posts array into another variable. Inside this map function you could use the Array.filter and Array.some methods to return the correct authors like so:

let mappedPosts = posts.map(post => {
    post.authors = authors.filter(author => post.authors.some(a => a === author.email));

    return post;
});

this would create the same array as your posts variable except that the authors array is now an array of objects with names and email properties.

By the way: while testing my approach, i noticed your posts array probably is not seperated correctly:

 { title: 'title one', authors: ['[email protected]', '[email protected], [email protected]'] },

Should be:

{ title: 'title one', authors: ['[email protected]', '[email protected]', '[email protected]'] },

(notice the comma's i placed behind [email protected] and before [email protected].

Edit

In react i would store the new variable in a state, while using the useEffect hook to look for changes in both the authors and posts array using the dependency array:

let [mappedPosts, setMappedPosts] = useState([]);

useEffect(() => {
    if (authors && posts) {
        setMappedPosts(posts.map(post => {
            post.authors = authors.filter(author => post.authors.some(a => a === author.email));
            return post;
        }));
    }
}, [authors, posts]);

basically the same except the React way. Then you could loop through mappedPosts and render the authors directly instead of having to filter through a different array inside your JSX which IMO is not very neat.

Edit 2

From what i understand is that you want to check if either the email or the actual name of the author has a match inside the authors array with respect to the posts array.

This is actually quite easy to do, you just have to extend your statement inside the some function:

let [mappedPosts, setMappedPosts] = useState([]);

useEffect(() => {
    if (authors && posts) {
        let foundPosts = posts.map(post => {
            post.authors = authors.filter(author => {
                return post.authors.some(a => {
                    return a === author.email || a === author.name;
                });
            });
            return post;
        });
        setMappedPosts(foundPosts);
    }
}, [authors, posts]);

If you didn't notice; i've changed up the code a little bit to make it more readable. (store the found posts inside a variable and pass that variable to the setState function instead of alltogether).

Upvotes: 1

Lakshya Thakur
Lakshya Thakur

Reputation: 8316

First we can build a map for each email as key and name as value and then use the same to get a new posts array like so :-

const authors = [{
    name: 'Thompson Smith',
    email: '[email protected]',
    userId: '001',
    avatar: '/avatar/1'
  },
  {
    name: 'John Doe',
    email: '[email protected]',
    userId: '002',
    avatar: '/avatar/2'
  },
  {
    name: 'Jane Coker',
    email: '[email protected]',
    userId: '003',
    avatar: '/avatar/3'
  },
  {
    name: 'Mirabel Ekong',
    email: '[email protected]',
    userId: '004',
    avatar: '/avatar/4'
  },
  {
    name: 'Samuel Doe',
    email: '[email protected]',
    userId: '005',
    avatar: '/avatar/5'
  },
  {
    name: 'Moses Philips',
    email: '[email protected]',
    userId: '006',
    avatar: '/avatar/6'
  },
  {
    name: 'Marcus Bowa',
    email: '[email protected]',
    userId: '007',
    avatar: '/avatar/7'
  },
  {
    name: 'Peter Touch',
    email: '[email protected]',
    userId: '008',
    avatar: '/avatar/8'
  },
  {
    name: 'Benson Bruce',
    email: '[email protected]',
    userId: '009',
    avatar: '/avatar'
  }
]

const posts = [{
    title: 'title one',
    authors: ['[email protected]', '[email protected]', '[email protected]']
  },
  {
    title: 'title two',
    authors: ['[email protected]', '[email protected]', '[email protected]']
  },
  {
    title: 'title three',
    authors: ['[email protected]', '[email protected]', '[email protected]']
  },
  {
    title: 'title four',
    authors: ['[email protected]', '[email protected]', '[email protected]']
  },
  {
    title: 'title five',
    authors: ['michael Johnson', '[email protected]', '[email protected]']
  },
  {
    title: 'title six',
    authors: ['michael Johnson', 'Jane Joshua', '[email protected]']
  },
]

const nameEmailMap = authors.reduce((acc, {
  name,
  email,
  userId,
  avatar
}) => {
  acc[email] = {
    name,
    userId,
    avatar
  };
  return acc;
}, {})

const newPosts = posts.map(({
  title,
  authors
}) => ({
  title,
  authors: authors.map(entry => (nameEmailMap[entry] ? { ...nameEmailMap[entry]
  } : {
    name: entry
  }))
}));

console.log(newPosts);

Upvotes: 0

Apostolos
Apostolos

Reputation: 10463

If the need is just to create a new array, then here is a sample that converts the array to the desired outcome

let authors = [
  {name: 'Thompson Smith', email: '[email protected]'},
  {name: 'John Doe', email: '[email protected]'},
  {name: 'Jane Coker', email: '[email protected]'},
  {name: 'Mirabel Ekong', email: '[email protected]'},
  {name: 'Samuel Doe', email: '[email protected]'},
  {name: 'Moses Philips', email: '[email protected]'},
  {name: 'Marcus Bowa', email: '[email protected]'},
  {name: 'Peter Touch', email: '[email protected]'},
  {name: 'Benson Bruce', email: '[email protected]'},
]

let posts = [
  { title: 'title one', authors: ['Michael Johnson', '[email protected]', '[email protected]'] },
  { title: 'title two', authors: ['[email protected]', '[email protected]', '[email protected]'] },
  { title: 'title three', authors: ['[email protected]', '[email protected]', '[email protected]'] },
  { title: 'title four', authors: ['[email protected]', '[email protected]', '[email protected]'] },
]

posts = posts.map(obj => ({ ...obj, authors: obj.authors.map(email => authors.find(auth => auth.email === email)?.name ? authors.find(auth => auth.email === email)?.name : email )}))

console.log(posts)

Upvotes: 0

Arminas Šaltenis
Arminas Šaltenis

Reputation: 166

You could have a separate method for filtering out authors and mapping their names like:

function findPostAuthors(post) {
    return authors
    .filter(({email}) => post.authors.includes(email))
    .map(({name}) => name);
}

Also, your posts.authors arrays, all have two items, you are missing ' between second and third item.

In react you could use it, something like this:

return (
    <div className="App">
      {posts.map((post) => (
        <div>
          <h2>{post.title}</h2>
          <p>
            {findPostAuthors(post)
              .map(author => (<span>{author}</span>))}
          </p>
        </div>
      ))}
    </div>
  );

This way you could ditch .map in findPostAuthors function, so it wouldn't loop too many times.

Upvotes: 0

Chris Farmer
Chris Farmer

Reputation: 25396

This seems like an exercise in javascript map and filter.

Here's an example of how you could do it. And a codesandbox: https://codesandbox.io/s/agitated-brown-owf67

Also your email address arrays are formatted weirdly, and since you didn't indicate a reason for that in your question I interpreted it as a typo and fixed them. YMMV.

  return (
    <div className="App">
      {posts.map((p) => (
        <div>
          <h2>{p.title}</h2>
          <p>
            {p.authors
              .map((a) =>
                authors
                  .filter((author) => author.email === a)
                  .map((author) => author.name)
              )
              .map(a => (<span>{a}</span>))}
          </p>
        </div>
      ))}
    </div>
  );

Upvotes: 1

Related Questions