[SOLVED] Problems adding attributes when maping arrays

Issue

This Content is from Stack Overflow. Question asked by ricardo leiva sikic

I am trying within the changeEventStatus function to add an isJoined attribute to true or false, depending on whether the user is already in that event or not. What I have thought of doing so far is to first filter between 2 arrays and then go through and compare the element of the original array versus the element of the filtered array using a condition.

The problem is that while the conditional works, it only takes 1 element as true and adds the attribute as true leaving all other elements in the array as false. In the filtered array there are 2 objects so it should return an array with 2 true elements within its attributes.

// React
import React, { useEffect, useState } from 'react';

// Chakra-ui
import {
  SimpleGrid,
  Box,
  Center,
  Text,
  Stack,
  Button,
  Spinner,
  Flex,
  useToast,
} from '@chakra-ui/react';

// Supabase
import { supabase } from '../../client';

const EventCard = () => {
  const toast = useToast();
  const [events, setEvents] = useState([]);
  const [isJoining, setIsJoining] = useState(false);
  const [eventId, setEventId] = useState(null);
  
  useEffect(() => {
    getEvents();
    changeEventStatus();
  }, []);
  
  const getEvents = async() => {
    const { data, error } = await supabase
    .from('events')
    .select('*')
    setEvents([...events, ...data]);
  }

  const toaster = () => {
    return(
      toast({
        title: '¬°Event Message!',
        description: "You already participate in this event.",
        status: 'error',
        duration: 3000,
        isClosable: true,
      })
    )
  }
  
  const isUserParticipateInEvent = async(userId, eventId) => {
    let { data: event_participants, error } = await supabase
    .from('event_participants')
    .select('*')
    .eq('participant_id', userId)
    .eq('event_id', eventId)
    if(event_participants.length > 0) {
      return true;
    } else {
      return false;
    }
  }
  
  const joinToEvent = async(id) => {
    const user = supabase.auth.user();
    let userId = user.id;
    let eventId = Number(id);
    
    setEventId(id);

    const participant = {
      participant_id: userId,
      event_id: eventId
    };

    let isParticipating = await isUserParticipateInEvent(userId, eventId);

    if(!isParticipating) {
      try {
        setIsJoining(true);
        const { data, error } = await supabase
        .from('event_participants')
        .insert([participant]);
        if(error) {
          console.log(error);
        }
      } catch(err) {
        console.log('Error trying to join the event ', err);
      } finally {
        setIsJoining(false);
      }
    } else {
      toaster();
    }
  }

  const changeEventStatus = async() => { // The problem
    const user = supabase.auth.user();
    const userId = user.id;
    const { data: event_participants, error } = await supabase
    .from('event_participants')
    .select('*')
    .eq('participant_id', userId)

    const { data:eventData, error:dataError } = await supabase
    .from('events')
    .select('*')
    
    let eventParticipantIds = event_participants.map((ep) => ep.event_id);
    let filteredEvents = eventData.filter((event) => eventParticipantIds.includes(event.id));
    console.log('filtered ', filteredEvents)

    eventData.map((evt) => {
      filteredEvents.map((filteredEvent) => {
        console.log('filt ', filteredEvent.id)
        console.log('event ', evt.id)
        console.log(filteredEvent.id === evt.id)
        if(filteredEvent.id === evt.id) {
          evt['isJoined'] = true;
        } else {
          evt['isJoined'] = false;
        }
      })
    });

    console.log('new event data to render', eventData)
  }

  return(

    events.map((event, ix) =>{
      return(
        <Box key={ix} p={4} rounded='sm' border='1px' borderColor='gray.100' boxShadow={'sm'}>
          <SimpleGrid display='flex' alignItems='center' justifyContent='space-between' columns={2}>
            <Box>
              <Stack>
                <Text fontSize={'2xl'} fontWeight='600'>{event.title}</Text>
                <Text>{event ? event.description : 'No description provided'}</Text>
                <Text>{`Organizer: ${event?.creator}`}</Text>
              </Stack>
              <Box pt={4}>
                <SimpleGrid columns={{sm: 1, md: 2}}>
                  <Box><Text fontSize={'small'}>{`20 participants`}</Text></Box>
                  <Box><Text fontSize={'small'}>{`Date: ${event.start_date}`}</Text></Box>
                </SimpleGrid>
              </Box>
            </Box>
            <Box>
              <Button bg='green.200' onClick={(e) => joinToEvent(event.id)}>
                {(isJoining && (eventId === event.id)) ? 'Joining...' : 'Join'}
              </Button>
            </Box>
            <Box>
              {(isJoining && (eventId === event.id)) && <Spinner />}
            </Box>
          </SimpleGrid>
        </Box>
      )
    })
  )
}

export default EventCard;



Solution

The problem is your nested loop. On each pass, it sets all but one element’s isJoined to false.

You can simplify this greatly using the following

const eventParticipantIds = new Set(
  event_participants.map((ep) => ep.event_id)
);

const transformedEventData = eventData.map((evt) => ({
  ...evt,
  isJoined: eventParticipantIds.has(evt.id),
}));

A Set is optimised for value lookups so it performs better than an array.


This Question was asked in StackOverflow by ricardo leiva sikic and Answered by Phil It is licensed under the terms of CC BY-SA 2.5. - CC BY-SA 3.0. - CC BY-SA 4.0.

people found this article helpful. What about you?