How do I match the union of two arrays in a v-for loop

Viewed 133

I have two arrays: users and projects. IDs in both are unique numbers. A project can have more than one owner, so in projects I have an array of user IDs named ownersId that link to the id of users in users, as seen here:

export const users = [{
    id: 1,
    givenName: 'Alexander',
    surname: 'Kelly',
    initials: 'AK'
}, {
    id: 2,
    givenName: 'Karen',
    surname: 'Jones',
    initials: 'KJ'
}, {
    id: 3,
    givenName: 'Casey',
    surname: 'Fields',
    initials: 'CF'
}, {
    id: 4,
    givenName: 'Sam',
    surname: 'Johns',
    initials: 'SJ'
}, {
    id: 5,
    givenName: 'Thomas',
    surname: 'Smith',
    initials: 'TS'
}, {
    id: 6,
    givenName: 'Jack',
    surname: 'Jenson',
    initials: 'JJ'
}];
export const projects = [{
    id: 1,
    name: 'Project 1',
    ownersId: [
        1,
        2,
        5,
        6,
    ]}, {
    id: 2,
    name: 'Project 2',
    ownersId: [
        1,
        3,
    ]}, {
    id: 3,
    name: 'Project 3',
    ownersId: [
        1,
        2,
        4,
        3, 
    ]}, {
    id: 4,
    name: 'Project 4',
    ownersId: [
        1,  
    ]}, {
}]

What I am trying to do is loop over the details of project which I have successfully done using v-for. What I am stuck on is displaying a loop within the loop that displays all the users names using the IDs in the ownersId field.

<template>
    <div class="demo">
        <div 
            v-for="project in projects"  
            v-bind:key="project.id" 
        >
            <h4><strong>{{ project.name }}</strong></h4>
            <div v-if="project.ownersId" >
                Shared with {{ project.ownersId.length }} others
            </div>
            <div>
                <!-- Loop of the list of names from IDs goes here -->
            </div>
        </div>
    </div>
</template>

<script>
import { projects } from '../data/example';
import { users }  from '../data/example';

export default {
    name: "Demo",
    data() {
        return {
            projects,
            users,
        }
    }
}
</script>
2 Answers

Convert your user data array to a computed object for fast lookup. Then there will only be 1 data iteration, to create the object. The object will also be cached because it's a computed so re-renders won't trigger the iteration again.

This could prevent a lot of excess array looping and finding, which might be important for performance with larger datasets, especially if the template is re-rendered for any reason.

computed: {
  userData() {
    const userData = {};
    this.users.forEach(user => {
      userData[user.id] = user;
    });
    return userData;
  }
}

Template

<div 
    v-for="project in projects"  
    v-bind:key="project.id" 
>
    <h4><strong>{{ project.name }}</strong></h4>
    <div v-if="project.ownersId" >
        Shared with {{ project.ownersId.length }} others
    </div>
    <div>
        <div v-for="userId in project.ownersId" :key="userId">
            {{ userData[userId] }}
        </div>
    </div>
</div>

Here's a demo

Welcome to SC.

Given that these are 2 arrays, I feel the best way is to simply make a method that returns the user data based on the project, then loop through that data.

methods:{
 getUserList(project){
  return project.ownersId.map(id=>
    users.find(user=>user.id===id)
   )
 }
}

then use this method like so

<div v-for="project in projects" :key="project.id">
...
  <!-- Loop of the list of names from IDs goes here -->
  <template v-for="user in getUserList(project)" :key="user.id"> 
    {{ user }}
  </template>
...
</div>
Related