Programa que muestra la frecuencia de una lista de compras

Iniciado por LuisUvtol, 9 Junio 2020, 16:40 PM

0 Miembros y 1 Visitante están viendo este tema.

LuisUvtol

Hola a todos, perdón por el retraso pero hasta ahorita tuve un poco de tiempo, les comparto mi programa, el cual dado una lista (separada por comas) calcula la frecuencia de esta misma, tambien lo guarda en un archivo de texto, les dejo el código, solo creen su archivo de texto, yo lo puse como "BaseDeDatos.txt", no es el mejor programa pero si funciona jeje
Código:
Código (cpp) [Seleccionar]
#include <stdio.h>  // printf
#include <stdlib.h> // malloc y free
#include <string.h> // strcasecmp
#include <conio.h>
#include <iostream>
#include <fstream>
#include <bits/stdc++.h>

using namespace std;

//Prototipos de funciones
void agregar(struct frecuenciaCompras indice);
void agregarLista(char id[100]);
void imprimir();
void guardar();
bool OrdenarPorIndice(const pair<int, int>& a, const pair<int, int>& b);
void OrdenarPorFrecuencia(int recibo[], int n);
void menu();

struct Nodo *superior = NULL;      //Como estara vacio entonces es Null

//Declaro Una estructura con datos ID y NumeroCompras
struct frecuenciaCompras{
char id[200];
int NumeroCompras;
};
//Declaro un nodo que me ayudara a crear mi pila
struct Nodo{                                //Declaro una Estructura la cual es un Nodo y contiene los valores de indice
struct frecuenciaCompras indice;
struct Nodo *sigue;                     //Apunta a un nodo siguiente por tratarse de una pila
};



//----------------------------------------------Funcion Main-----------------------------------------------------
int main(){

cout<<"\tSupermercado \n\n " <<endl;
menu();

getch();


}
//----------Declaracion de las funciones--------------------
void agregar(struct frecuenciaCompras indice){                          //Esta funcion hace un push, es decir, agrega un struct
 struct Nodo *agregaNodo = (Nodo*)malloc(sizeof(struct Nodo));
 agregaNodo->indice = indice;
 agregaNodo->sigue = superior;
 superior = agregaNodo;
}
void buscarPorFrecuencia(char id[200]){                    //Esta funcion recorre toda la pila y compara cada caracter, si encuentra un caracter repetido la frecuencia aumenta
struct Nodo *aux = superior;
while (aux != NULL){
int buscafrecuencia = strcasecmp(aux->indice.id, id);
if(buscafrecuencia==0){
aux->indice.NumeroCompras++; //Si se repite entonces se le sumara 1 al numero de compras
return;
}
aux = aux->sigue;
}
struct frecuenciaCompras indice;
strcpy(indice.id, id);
indice.NumeroCompras = 1;                               //Si solo se repite 1 vez entonces el numero de compras es 1
agregar(indice);
}

void imprimir(){
 char guiones[] = "---------------------";
 printf("%s%s\n", guiones, guiones);
 printf("|%-20s|%-20s|\n", "ID producto", "Veces Comprado");
 printf("%s%s\n", guiones, guiones);

struct Nodo *aux = superior;

while (aux != NULL){
printf("|%-20s|%-20d|\n", aux -> indice.id, aux->indice.NumeroCompras);
aux = aux -> sigue;                                                         //Con esto imprimo el que sigue
}

printf("%s%s\n", guiones, guiones);

}

void guardar(){
   ofstream archivo;                                      //Se ejecuta la funcion ofstream
char fecha[15];

cout<<"Fecha de Guardado: ";                            //Se le pide al usuario que digite la fecha
cin.ignore();
cin.getline(fecha,15, '\n');

archivo.open("BaseDatos.txt",ios::app);                 //Se abre el archivo
archivo<<"Fecha de guardado: "<<fecha<<"\n";            //se escribe la fecha de guardado del archivo

struct Nodo *aux = superior;                                   //Creo una variable auxiliar y la iguala a superior

char guion[] = "--------------------";                  //Apartado unicamente para la interfaz grafica

   archivo<<"+"<<guion<<"+"<<guion<<"+\n";
   archivo<<"   Producto          |     Frecuencia     \n";
   archivo<<"+"<<guion<<"+"<<guion<<"+\n";

while(aux != NULL){                                     //Se declara un bucle para guardar los datos
archivo<<aux->indice.id<<"                             "<<aux->indice.NumeroCompras<<"\n";  //accedo a los valores de mi estructura indice y el numero de compras
aux = aux->sigue;
}

archivo<<"+"<<guion<<"+"<<guion<<"+\n";                 //Apartado para graficos
   archivo.close();                                        //Cierro mi archivo
cout<<"Base de datos actualizada\n\n";
}


//------------------------------Esta funcion me ordena mi cadena de enteros dependiendo de la frecuencia-----------------------------------------
//Si tengo una lista de enteros: 4,5,5,6,6,6 me la ordena de la siguiente forma: 6 6 6 5 5 4
unordered_map<int, int> valor;                                              //unordered_map almacena elementos formados por la combinacion de un valor clave y un valor mapeado

bool OrdenarPorIndice(const pair<int, int>& a, const pair<int, int>& b){

   if (a.second == b.second)
       return valor[a.first] < valor[b.first];

   return a.second > b.second;
}

void OrdenarPorFrecuencia(int recibo[], int n){                   //Funcion que me ordena, recibe una lista de enteros, el int n solo sirve para los bucles for{
   unordered_map<int, int> mapaa;
   vector<pair<int, int> > vec;                                //El pair consta de 2 elementos, el primero elemento se hace referencia como primero(first) y el segundo(second)

   for (int i = 0; i < n; ++i) {
       mapaa[recibo[i]]++;

       if (valor[recibo[i]] == 0) // Actualiza el valor del mapa solo una vez
           valor[recibo[i]] = i + 1;
   }


   copy(mapaa.begin(), mapaa.end(), back_inserter(vec));      //copio mapa al vector mediante la funcion copy

   sort(vec.begin(), vec.end(), OrdenarPorIndice); //sort me  sirve para ordenar el vector en orden ascendente (por indice)
   int elj; //declaro una variable int la cual convertire a char
   string je;
   for (int i = 0; i < vec.size(); ++i) { //Declaro una sentencia for anidada el primer for es para recorrer el vector y el sgundo para ir guardando los datos en mi variable elj
       for (int j = 0; j < vec[i].second; ++j){
           elj = vec[i].first;
//----------------------Convierto el int a string ------------------
           stringstream ss;
           ss<<elj;
           string s;
           string space(" ");                              //Dejo un espacio por cada valor
           ss>>s;
           s.append(space);                               //Concateno los string
           je.append(s);

}
}
//-------------------------Convierto de string a char---------------
char c[je.size()+1];
strcpy(c, je.c_str());
   cout<<"La lista ordenda por frecuencia es: "<<c<<endl;
//strrev(c);   //se invierte el orden de la cadena
               char delimitador[] = ", ";                     //Esto me indica que al presentarse una coma o espacio se dividara la cadena
char *token = strtok(c, delimitador);           //tokenizo mi char con la funcion strtok
while (token != NULL){                      //Establesco un While para ingresar token por token a mi pila
buscarPorFrecuencia(token);
token = strtok(NULL, delimitador);
}

cout << "Lista agregada correctamente\n\n";

}

//------------------------Menu interactivo------------------------------------------
void menu(){
int opcion;
do{
cout<<"\t Proyecto Supermercado\n";
cout<<"(1) Insertar lista\n";
cout<<"(2) Ver Tabla\n";
cout<<"(3) Guardar Datos\n";
cout<<"Ingrese una opcion: ";
cin>>opcion;

switch(opcion){
case 1:{    int lista[] = { 30,10,20,90, 30,30,40,40,40, 55,55,75,75,100,55,55 };       //Se ingresan las listas
int bucle = sizeof(lista) / sizeof(lista[0]);                    // sizeof (lista) devuelve el número de bytes que ocupa la matriz. Como cada elemento puede ocupar más de 1 byte de espacio,
                                                                                           //se divide el resultado con el tamaño de un elemento (sizeof (lista [0])). Esto le da un número de elementos en la matriz.
OrdenarPorFrecuencia(lista, bucle);                                 //Llamo a mi funcion y envio mi lista y mi n

}break;

case 2:{ cout <<"Mostrando Datos \n";
imprimir();                                               //Llamo a mi funcion imprimir
}break;
case 3:{guardar();                                                //Llamo a mi funcion guardar
}break;
}
}while(opcion!=4);



}

K-YreX

Por comentar algunas cosas del código, diré lo siguiente:

Bibliotecas


  • Si trabajas con C++, utiliza siempre <iostream> para entradas/salidas y no lo mezcles con <stdio.h> que es propia de C.
  • Las biblioteca propias de C (acabadas en .h) tienen su propia versión para C++ (eliminando el .h e incluyendo una c al principio. Ej -> <cstdio>).
  • En C++ el uso de memoria dinámica no se realiza con malloc() / free() sino con new() / delete().
  • La biblioteca <conio.h> no pertenece al estándar y por tanto hay máquinas en las que da problemas. La función típica de esta biblioteca es getch() que puede y debe ser sustituida por cin.get() en C++ (<iostream>) o por getchar() en C (<stdio.h>).



    Novedades de C++ frente a C


  • La diferencia fundamental entre C/C++ es la capacidad del segundo de utilizar el paradigma de programación orientado a objetos. Aunque C permite el uso de structs, están son más limitadas que las clases de C++ ya que no permiten utilizar el principio de ocultación (declarar miembros privados). Usando clases con sus miembros privados y métodos o funciones miembro públicas para interactuar con los miembros de la clase, aumentas la seguridad del programa.
  • Además, como curiosidad, me parece que en C++ se puede omitir la palabra struct si no me equivoco cada vez que se hace referencia a ese tipo de dato.
  • Otra ventana de C++ es la aparición de strings. Sobre todo para facilitar el trabajo con memoria dinámica y no tener que desperdiciar espacio reservando los char[] de manera estática. Aprovecho ahora que hablo sobre las cadenas de char para decir que cuando se define una cadena char como parámetro a una función no es necesario aportar su longitud siempre que sea de una dimensión. Son números que no aportan nada al programa y que pueden complicar las labores de modificación del código. Y las longitudes que sí tienen que estar presentes es mejor guardarlas como constantes para facilitar su modificación y mantenimiento.



    Organización del Código


  • Normalmente cuando ves un programa en C/C++, si quieres ver lo que hace de un vistazo rápido, buscas la función main(). En este caso esa función no aporta nada a cualquier persona que quiera leer el código. Hay que tener un término medio entre usar funciones y no usarlas. La función menu() debería mostrar las opciones, controlar que la opción es válida (opcional) y devolver la opción elegida. Y en el main() debería llamarse a esa función y a continuación tener el código principal, es decir, el switch con las opciones.
  • Además de eso, otro mal hábito es utilizar variables globales. El uso de variables globales es excepcional y casi siempre son constantes lo que se declaran de esta manera. Las variables deberían ser locales a la función en la que van a ser necesitadas.



    No me enrollo más. Esas son algunas, si no todas las cosas que yo mejoraría de ese código.
    Por lo demás es un código limpio y con comentarios que explican las cosas que se hacen.

    Mucha suerte en otros proyectos. :-X
Código (cpp) [Seleccionar]

cout << "Todos tenemos un defecto, un error en nuestro código" << endl;

Loretz

Algo que yo modificaría es la función OrdenarPorFrecuencia()

Primero haría que hiciese una sola cosa, si dice que lo que hace es ordenar la lista por frecuencia, que haga eso y nada más. Luego vendrá otra función que la muestre.

En segundo lugar, revisaría los algoritmos buscando algo más directo, más claro y, de ser posible, más eficiente. Quiero decir, le pegaría duro.