Ejercicio en C++ con archivo CSV, listas ligadas y disponibilidad de camas

En este post te mostraré la solución a un ejercicio en C++ que tiene que ver con archivos CSV y disponibilidad de camas en hospitales.

Básicamente es leer un archivo CSV, pasarlo a la lista enlazada (estructura de datos) y luego generar estadísticas, eliminar repetidos, eliminar un registro y borrarlo.

Nos vamos a basar ampliamente en dos tutoriales que ya he publicado en mi blog: leer archivos CSV con C++ y listas enlazadas en C++.

Descripción del ejercicio

La Secretaría Regional de cierto lugar del Ministerio de Salud cuenta con datos almacenados dentro de un archivo CSV que registra las camas UCI ocupadas diariamente a nivel regional.

Este archivo representa los datos mediante la siguiente estructura de 3 campos: fecha;codigo_hospital;numero_de_cama

El valor del campo fecha corresponde a la fecha (en formato aaaammdd) en que fue generado el registro.

El valor del campo codigo_hospital corresponde a un identificador único para cada hospital de la región y el valor del campo numero_de_cama es la identificación de la misma.

Al respecto, una cama es única en un hospital; si el mismo número se repite en dos hospitales distintos se trata, entonces, de dos camas diferentes.

Requerimientos

Usted debe implementar un programa en lenguaje C++ que dé cobertura a los siguientes requerimientos:

  1. leeProcesa(string, Lista): Al iniciarse la ejecución del programa, este invoca a la función leeProcesa que leerá el contenido del archivo CSV y almacenará su contenido completo en la memoria utilizando para su almacenamiento una lista de tipo LLS.
  2. nuevaListaSinDuplicados(Lista, Lista): Escribir el operador nuevaListaSinDuplicados que partir de la LLS genera una nueva LLS sin repetidos, dejando sólo una ocurrencia de cada información presente en la lista (A partir del punto 3, esta nueva lista será la que trabajaremos).
  3. seleccionar(Lista, int, Lista): Proveer la funcionalidad seleccionar, que a partir de los datos contenidos en una LLS como la creada en el punto 2 y un código de un hospital pasado como parámetro, creará una nueva LLS únicamente con los datos de las camas del hospital referido.
  4. estadistico(Lista): Proveer la funcionalidad estadístico, que dada una LLS permitirá mostrar en pantalla la cantidad de camas registradas por cada hospital. A modo de ejemplo:
    1. hospital 17 tiene en total 112 cama(s) distintas.
    2. hospital 14 tiene en total 16 cama(s) distintas.
    3. hospital 15 tiene en total 18 cama(s) distintas.
  5. contar(Lista, int, int, int): Proveer la funcionalidad contar, que dada una LLS y una fecha (dia, mes, anho) retorna la cantidad de camas ocupadas existentes de esa fecha.
  6. eliminarHospital(Lista, int): Proveer la funcionalidad eliminarHospital, que dada una LLS y un identificador de hospital eliminará todas las ocurrencias contenidas en la LLS asociadas a dicho hospital.

Proveer de un menú de diálogo para el usuario que le permita acceder a las funciones definidas en los puntos anteriores.

  • Crear Lista desde archivo
  • Crear Lista sin duplicados
  • Crear Lista de un Hospital
  • Mostrar estadísticas de número de camas por Hospital
  • Cantidad de camas ocupadas por fecha especificada.
  • Eliminar registros de camas de un hospital
  • Salir

Nota: Al seleccionar cada opción, debe mostrar la lista respectiva

Explicación del algoritmo

Necesitamos la clase Cama que irá dentro de la clase Nodo. Y la clase Lista que maneja los nodos. También necesitamos ciertos métodos que nos permitan eliminar los nodos ya sea por cama (para los duplicados) o por código de hospital.

Igualmente en el constructor de la clase Cama vamos a necesitar separar la fecha por año, mes y día. Para ello cortamos con substr y luego convertimos a entero con stoi.

Código completo

Una vez que he explicado los términos generales veamos el código. En este caso primero tenemos el de la lista:

/*

  ____          _____               _ _           _       
 |  _ \        |  __ \             (_) |         | |      
 | |_) |_   _  | |__) |_ _ _ __ _____| |__  _   _| |_ ___ 
 |  _ <| | | | |  ___/ _` | '__|_  / | '_ \| | | | __/ _ \
 | |_) | |_| | | |  | (_| | |   / /| | |_) | |_| | ||  __/
 |____/ \__, | |_|   \__,_|_|  /___|_|_.__/ \__, |\__\___|
         __/ |                               __/ |        
        |___/                               |___/         
    
____________________________________
/ Si necesitas ayuda, contáctame en \
\ https://parzibyte.me               /
 ------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
Creado por Parzibyte (https://parzibyte.me).
------------------------------------------------------------------------------------------------
            | IMPORTANTE |
Si vas a borrar este encabezado, considera:
Seguirme: https://parzibyte.me/blog/sigueme/
Y compartir mi blog con tus amigos
También tengo canal de YouTube: https://www.youtube.com/channel/UCroP4BTWjfM0CkGB6AFUoBg?sub_confirmation=1
Twitter: https://twitter.com/parzibyte
Facebook: https://facebook.com/parzibyte.fanpage
Instagram: https://instagram.com/parzibyte
Hacer una donación vía PayPal: https://paypal.me/LuisCabreraBenito
------------------------------------------------------------------------------------------------
*/
#include <iostream>
using namespace std;
class Cama
{
public:
    string fecha;
    int codigoHospital;
    string numeroCama;
    int dia, mes, anho;

    Cama(string fecha, string codigoHospital, string numeroCama)
    {
        this->fecha = fecha;
        this->codigoHospital = stoi(codigoHospital);
        this->numeroCama = numeroCama;
        // Extraer la fecha desde el constructor
        this->anho = stoi(fecha.substr(0, 4));
        this->mes = stoi(fecha.substr(4, 2));
        this->dia = stoi(fecha.substr(6, 2));
    }

    void imprimir()
    {
        cout << "Fecha: " << this->fecha << ". Codigo hospital: " << this->codigoHospital << ". Numero cama: " << this->numeroCama << endl;
    }

    bool equivale(Cama *otraCama)
    {
        if (otraCama == NULL)
        {
            return false;
        }
        return this->fecha == otraCama->fecha && this->codigoHospital == otraCama->codigoHospital && this->numeroCama == otraCama->numeroCama;
    }
};
class Nodo
{
public:
    Cama *cama;
    Nodo *siguiente;
    Nodo(Cama *cama)
    {
        this->cama = cama;
        this->siguiente = NULL;
    }
};

class Lista
{

private:
    void agregarRecursivo(Nodo *n, Cama *dato)
    {
        if (n->siguiente == NULL)
        {
            n->siguiente = new Nodo(dato);
        }
        else
        {
            this->agregarRecursivo(n->siguiente, dato);
        }
    }

    void imprimirRecursivo(Nodo *n)
    {
        if (n != NULL)
        {
            n->cama->imprimir();
            this->imprimirRecursivo(n->siguiente);
        }
    }
    void eliminarRecursivo(Nodo *n, int codigoHospital)
    {
        if (n == NULL)
        {
            return;
        }
        if (n->cama->codigoHospital == codigoHospital && n == this->cabeza)
        {
            Nodo *temporal = this->cabeza;
            if (this->cabeza->siguiente != NULL)
            {
                this->cabeza = this->cabeza->siguiente;
                delete temporal;
            }
            else
            {
                this->cabeza = NULL;
            }
            return;
        }
        if (n->siguiente != NULL && n->siguiente->cama->codigoHospital == codigoHospital)
        {
            Nodo *temporal = n->siguiente;
            if (n->siguiente != NULL)
            {
                n->siguiente = n->siguiente->siguiente;
            }
            delete temporal;
        }
        else
        {
            this->eliminarRecursivo(n->siguiente, codigoHospital);
        }
    }

    void eliminarRecursivo(Nodo *n, Cama *camaParaEliminar)
    {
        if (n == NULL)
        {
            return;
        }
        if (n->cama->equivale(camaParaEliminar) && n == this->cabeza)
        {
            Nodo *temporal = this->cabeza;
            if (this->cabeza->siguiente != NULL)
            {
                this->cabeza = this->cabeza->siguiente;
                delete temporal;
            }
            else
            {
                this->cabeza = NULL;
            }
            return;
        }
        if (n->siguiente != NULL && n->siguiente->cama->equivale(camaParaEliminar))
        {
            Nodo *temporal = n->siguiente;
            if (n->siguiente != NULL)
            {
                n->siguiente = n->siguiente->siguiente;
            }
            delete temporal;
        }
        else
        {
            this->eliminarRecursivo(n->siguiente, camaParaEliminar);
        }
    }
    bool existeRecursivo(Nodo *n, Cama *camaBuscada)
    {
        if (n == NULL)
        {
            return false;
        }
        if (n->cama->equivale(camaBuscada))
        {
            return true;
        }
        return this->existeRecursivo(n->siguiente, camaBuscada);
    }
    bool existeRecursivo(Nodo *n, int codigoHospital)
    {
        if (n == NULL)
        {
            return false;
        }
        if (n->cama->codigoHospital == codigoHospital)
        {
            return true;
        }
        return this->existeRecursivo(n->siguiente, codigoHospital);
    }

public:
    Nodo *cabeza;
    void copiaSinDuplicados(Lista *l)
    {
        Nodo *temporal = this->cabeza;
        while (temporal != NULL)
        {
            if (!l->existe(temporal->cama))
            {
                l->agregar(temporal->cama);
            }
            temporal = temporal->siguiente;
        }
    }
    void eliminar(Cama *cama)
    {
        this->eliminarRecursivo(this->cabeza, cama);
    }
    void eliminar(int codigoHospital)
    {
        this->eliminarRecursivo(this->cabeza, codigoHospital);
    }
    void agregar(Cama *cama)
    {
        if (this->cabeza == NULL)
        {
            this->cabeza = new Nodo(cama);
        }
        else
        {
            this->agregarRecursivo(this->cabeza, cama);
        }
    }

    void imprimir()
    {
        std::cout << "Imprimiendo lista..." << std::endl;
        this->imprimirRecursivo(this->cabeza);
    }
    bool existe(Cama *camaBuscada)
    {
        return this->existeRecursivo(this->cabeza, camaBuscada);
    }

    bool existe(int codigoHospital)
    {
        return this->existeRecursivo(this->cabeza, codigoHospital);
    }
};

Luego tenemos el código principal en donde incluimos al archivo anteriormente mostrado, implementamos las funciones y el menú que se solicitó.

/*

  ____          _____               _ _           _       
 |  _ \        |  __ \             (_) |         | |      
 | |_) |_   _  | |__) |_ _ _ __ _____| |__  _   _| |_ ___ 
 |  _ <| | | | |  ___/ _` | '__|_  / | '_ \| | | | __/ _ \
 | |_) | |_| | | |  | (_| | |   / /| | |_) | |_| | ||  __/
 |____/ \__, | |_|   \__,_|_|  /___|_|_.__/ \__, |\__\___|
         __/ |                               __/ |        
        |___/                               |___/         
    
____________________________________
/ Si necesitas ayuda, contáctame en \
\ https://parzibyte.me               /
 ------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
Creado por Parzibyte (https://parzibyte.me).
------------------------------------------------------------------------------------------------
            | IMPORTANTE |
Si vas a borrar este encabezado, considera:
Seguirme: https://parzibyte.me/blog/sigueme/
Y compartir mi blog con tus amigos
También tengo canal de YouTube: https://www.youtube.com/channel/UCroP4BTWjfM0CkGB6AFUoBg?sub_confirmation=1
Twitter: https://twitter.com/parzibyte
Facebook: https://facebook.com/parzibyte.fanpage
Instagram: https://instagram.com/parzibyte
Hacer una donación vía PayPal: https://paypal.me/LuisCabreraBenito
------------------------------------------------------------------------------------------------
*/
#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <algorithm>

#include "lista.cpp"

using namespace std;

void leeProcesa(string nombreArchivo, Lista *lista)
{
    ifstream archivo(nombreArchivo);
    string linea;
    char delimitador = ';';
    // Leemos la primer línea para descartarla, pues es el encabezado
    getline(archivo, linea);
    // Leemos todas las líneas
    while (getline(archivo, linea))
    {
        stringstream stream(linea); // Convertir la cadena a un stream
        string fecha, codigoHospital, numeroCama;
        // Extraer todos los valores de esa fila
        getline(stream, fecha, delimitador);
        getline(stream, codigoHospital, delimitador);
        getline(stream, numeroCama, delimitador);
        lista->agregar(new Cama(fecha, codigoHospital, numeroCama));
    }
    archivo.close();
}

void nuevaListaSinDuplicados(Lista *original, Lista *sinDuplicados)
{
    original->copiaSinDuplicados(sinDuplicados);
}

void seleccionar(Lista *lista, int codigoHospital, Lista *destino)
{
    Nodo *temporal = lista->cabeza;
    while (temporal != NULL)
    {
        if (temporal->cama->codigoHospital == codigoHospital)
        {
            destino->agregar(temporal->cama);
        }
        temporal = temporal->siguiente;
    }
}

void estadistico(Lista *lista)
{
    // Primero vamos a extraer códigos únicos para los hospitales, ya que no sabemos
    // si van en orden, si son consecutivos y tampoco sabemos los límites
    std::vector<int> codigos;
    Nodo *temporal = lista->cabeza;
    while (temporal != NULL)
    {
        int codigoHospital = temporal->cama->codigoHospital;
        bool existe = find(codigos.begin(), codigos.end(), codigoHospital) != codigos.end();
        if (!existe)
        {
            codigos.push_back(codigoHospital);
        }
        temporal = temporal->siguiente;
    }
    // Ahora recorremos el vector y generamos estadísticas

    for (std::size_t i = 0; i < codigos.size(); i++)
    {
        int codigo = codigos[i];
        int conteo = 0;
        // Recorremos la lista de nuevo por cada código encontrado
        Nodo *temporal = lista->cabeza;
        while (temporal != NULL)
        {
            int codigoHospital = temporal->cama->codigoHospital;
            if (codigoHospital == codigo)
            {
                conteo++;
            }
            temporal = temporal->siguiente;
        }
        cout << "Hospital " << codigo << " tiene en total " << conteo << " cama(s) distintas" << endl;
    }
}

int contar(Lista *lista, int anho, int mes, int dia)
{
    Nodo *temporal = lista->cabeza;
    int conteo = 0;
    while (temporal != NULL)
    {
        if (temporal->cama->anho == anho && temporal->cama->mes == mes && temporal->cama->dia == dia)
        {
            conteo++;
        }
        temporal = temporal->siguiente;
    }
    return conteo;
}
void eliminarHospital(Lista *lista, int codigoHospital)
{
    while (lista->existe(codigoHospital))
    {
        lista->eliminar(codigoHospital);
    }
}

void menu()
{
    string seleccion = "";
    Lista *listaConPosiblesRepetidos = new Lista();
    Lista *listaSinRepetidos = new Lista();
    while (seleccion != "g")
    {
        cout << "a. Crear lista desde archivo " << endl;
        cout << "b. Crear lista sin duplicados" << endl;
        cout << "c. Crear lista de un hospital" << endl;
        cout << "d. Mostrar estadisticas de numero de camas por hospital" << endl;
        cout << "e. Cantidad de camas ocupadas por fecha especificada" << endl;
        cout << "f. Eliminar registro de camas de un hospital" << endl;
        cout << "g. Salir" << endl;
        cout << "Seleccione: " << endl;
        cin >> seleccion;
        if (seleccion == "a")
        {
            leeProcesa("camas_ocupadas.csv", listaConPosiblesRepetidos);
            cout << "Lista leida desde archivo: " << endl;
            listaConPosiblesRepetidos->imprimir();
        }
        if (seleccion == "b")
        {
            nuevaListaSinDuplicados(listaConPosiblesRepetidos, listaSinRepetidos);
            cout << "Lista sin duplicados: " << endl;
            listaSinRepetidos->imprimir();
        }
        if (seleccion == "c")
        {
            int codigo;
            cout << "Ingresa el codigo de hospital: " << endl;
            cin >> codigo;
            Lista *deHospital = new Lista();
            seleccionar(listaSinRepetidos, codigo, deHospital);
            cout << "Datos del hospital solicitado: " << endl;
            deHospital->imprimir();
        }
        if (seleccion == "d")
        {
            cout << "Estadisticas de la lista:" << endl;
            estadistico(listaSinRepetidos);
        }
        if (seleccion == "e")
        {
            int anho, mes, dia;
            cout << "Ingresa el anho: " << endl;
            cin >> anho;
            cout << "Ingresa el mes: " << endl;
            cin >> mes;
            cout << "Ingresa el dia: " << endl;
            cin >> dia;
            int conteo = contar(listaSinRepetidos, anho, mes, dia);
            cout << "Camas ocupadas en fecha: " << conteo << endl;
        }
        if (seleccion == "f")
        {
            int codigo;
            cout << "Ingresa el codigo de hospital para eliminar: " << endl;
            cin >> codigo;
            eliminarHospital(listaSinRepetidos, codigo);
            cout << "Lista despues de haber eliminado el hospital: " << endl;
            listaSinRepetidos->imprimir();
        }
    }
}

int main()
{
    menu();
    return 0;
}

Por cierto, el archivo CSV de ejemplo es el siguiente:

fecha;codigo_hospital;numero_de_cama
20210101;20;214
20210101;20;214
20210101;15;197
20210101;16;296
20210101;16;108
20210101;17;152
20210101;20;291
20210101;14;249
20200303;14;105
20210101;16;177
20210101;16;292
20210103;19;160
20210101;16;155
20210101;16;107
20210101;16;107
20200101;11;179
20200101;11;260

Finalmente puedes leer más sobre C++ en el blog de Parzibyte.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *