Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mohammed almakki exercies #88

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions exercises-cpp/mohammed_almakki/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
cmake_minimum_required ( VERSION 3.14 )

project(newstarter)

add_subdirectory ( ex01_basics )
add_subdirectory ( ex02_oo_basics )
3 changes: 3 additions & 0 deletions exercises-cpp/mohammed_almakki/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This directory contains the template CMake projects for each of the exercises.

Do not write your code for the exercises in here. Instead, make a copy of the directory containing this file and name your new directory `firstname_surname`. The CMake files contain relative paths so the new directory should be at the same level as the parent `template` directory.
28 changes: 28 additions & 0 deletions exercises-cpp/mohammed_almakki/ex01_basics/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
cmake_minimum_required ( VERSION 2.8.5 )

###############################################################################
# Setup
###############################################################################
# Name
project ( WordCounter )

# Find custom cmake modules
set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../CMake")

# Common setup
include ( CommonSetup )

###############################################################################
# Executable
###############################################################################

set ( SRC_FILES
src/main.cpp
# add any additional source files here
)

set ( HDR_FILES
# add any additional header files here
)

add_executable ( WordCounter ${SRC_FILES} ${HDR_FILES} )
123 changes: 123 additions & 0 deletions exercises-cpp/mohammed_almakki/ex01_basics/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <regex>
#include <unordered_map>
#include <cstring>
#include <iomanip>

std::string readFile(const std::string &filepath)
{
std::ifstream inputStream(filepath);

if (inputStream)
{
std::ostringstream ss;
ss << inputStream.rdbuf();
return ss.str();
}
else
{
throw std::invalid_argument("Invalid path. Please make sure the file path is correct and the file exists.");
}
}

std::vector<std::string> tokenizeFile(const std::string &file)
{
std::regex re("[-\\s]");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the inent of this that the experssion match a hyphen of a whitespace, but a double backslash exits the special character.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This regex is used to split the string by two delimiters, the space, and the hyphen. It will match a space or a hyphen. After that, the expression is used in a reverse way, to get all the words that don't match it i.e. the split words.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A Hyphen is also a special character and will need to be exited as well

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it will be removed. For example, the string "lines are hard-coded" will be transformed into a vector of ["lines", "are", "hard", "coded"] by this expression.

std::sregex_token_iterator first{file.begin(), file.end(), re, -1}, last;
std::vector<std::string> tokens{first, last};
return tokens;
}

std::unordered_map<std::string, uint32_t> countWords(const std::vector<std::string> words)
{
std::unordered_map<std::string, uint32_t> wordsCounts;
for (auto word : words)
{
std::transform(
word.begin(), word.end(), word.begin(),
[](unsigned char c)
{ return std::tolower(c); });
word.erase(std::remove_if(word.begin(), word.end(), ispunct), word.end());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The task specifies only needing to consider .,?'"!(): you might have considered using regex to strip these away and replacing them with a whitespace at the same time as doing the hyphen.

Copy link
Author

@MohamedAlmaki MohamedAlmaki Dec 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understood, the comparison should be punctation insensitive. So for example, "what's" should be equal to "whats" but replacing it with whitespace will result in the word not being included (not longer than 4). So in this line, I just remove every punctuation mark.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case I think you may be right

if (word.length() > 4)
{
wordsCounts[word]++;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should have a check to see if word is currently in the wordCounts map

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, why is it needed?
If there is a get operation, then I need a check, but this is just used as a counter.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ultimatly it is not required for it to work, but it is better practice to have the check when performing an action on an object in a map where it is not certain that the object will exist.

}
}
return wordsCounts;
}

void sortAndWriteWords(const std::unordered_map<std::string, uint32_t> &wordsCounts, const std::string &outputDir)
{
std::vector<std::pair<std::string, int>> wordsCountsPairs;
for (auto &pair : wordsCounts)
{
wordsCountsPairs.push_back(pair);
}
std::sort(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good sorting process.

wordsCountsPairs.begin(), wordsCountsPairs.end(),
[](auto &left, auto &right)
{
return left.second > right.second;
});

std::ofstream outputFile(outputDir + "\\output.txt");

outputFile << std::setw(20) << std::left << "Word" << std::setw(20) << std::left << "Usage"
<< "\n\n";

for (auto &it : wordsCountsPairs)
{
outputFile << std::setw(20) << std::left << it.first << std::setw(20) << std::left << it.second << "\n";
}

outputFile.close();
std::cout << "Found " << wordsCountsPairs.size() << " words and their counts have been successfully written in this path: " << outputDir + "\\output.txt"
<< "\n";
}

int main(int, char **)
{
while (true)
{
std::cout << "Welcome to the Word Counter program!"
<< "\n\n";
std::cout << "This program will count the number of occurrences of unique words (longer than 4 characters and split hyphenated words, treating each part as different words)"
<< "\n";
std::cout << "The count will be case and punctuation insensitive"
<< "\n\n";
std::cout << "Please enter the path of the file:"
<< "\n";
std::string inputFilePath;
getline(std::cin, inputFilePath);

std::string file;
try
{
file = readFile(inputFilePath);
if (file.empty())
{
std::cout << "Note: "
<< "This file is empty so the output will be an empty file."
<< "\n";
}
}
catch (std::invalid_argument e)
{
std::cerr << "Error:" << e.what() << "\n\n";
continue;
}

std::vector<std::string> tokenizedFile = tokenizeFile(file);
std::unordered_map<std::string, uint32_t> wordsCounts = countWords(tokenizedFile);

std::string outputFileDir = inputFilePath.substr(0, inputFilePath.rfind('\\'));
sortAndWriteWords(wordsCounts, outputFileDir);

break;
}

return 0;
}
29 changes: 29 additions & 0 deletions exercises-cpp/mohammed_almakki/ex02_oo_basics/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
cmake_minimum_required ( VERSION 2.8.5 )

###############################################################################
# Setup
###############################################################################
# Name
project ( Shapes )

# Find custom cmake modules
set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../CMake")

# Common setup
include ( CommonSetup )

###############################################################################
# Executable
###############################################################################

set ( SRC_FILES
src/main.cpp
src/ShapeSorter.cpp
)

set ( HDR_FILES
src/Shape.h
src/ShapeSorter.h
)

add_executable ( Shapes ${SRC_FILES} ${HDR_FILES} )
133 changes: 133 additions & 0 deletions exercises-cpp/mohammed_almakki/ex02_oo_basics/src/Shape.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#pragma once

#include <string>
#include <math.h>

enum class ShapeType { Shape, Rectangle, Square, Circle, Triangle };

class Shape
{
public:
virtual inline double getArea() const = 0;
virtual inline double getPerimeter() const = 0;

virtual inline ShapeType getType() const = 0;
virtual inline std::string toString() const = 0;

inline int getSidesCount() const {
return noOfSides;
}

Shape(const int& noOfSides) : noOfSides{noOfSides} {}

private:
const int noOfSides;
};

class Rectangle : public Shape
{
public:
Rectangle(const double& side1, const double& side2) : side1{side1}, side2{side2}, Shape(2) {}

inline ShapeType getType() const override
{
return ShapeType::Rectangle;
}

inline double getArea() const override
{
return side1 * side2;
}

inline double getPerimeter() const override
{
return 2 * side1 + 2 * side2;
}

inline std::string toString() const override
{
return "Rectangle(side1=" + std::to_string(side1) + ", side2=" + std::to_string(side2) + ")";
}

private:
double side1;
double side2;
};

class Square : public Rectangle
{
public:
Square(double side1) : side1{side1}, Rectangle(side1, side1) {}

inline ShapeType getType() const override
{
return ShapeType::Rectangle;
}

inline std::string toString() const override
{
return "Square(side=" + std::to_string(side1) + ")";
}

private:
double side1;
};

class Circle : public Shape
{
public:
Circle(double radius) : radius{radius}, Shape(0) {}

inline ShapeType getType() const override
{
return ShapeType::Circle;
}

inline double getArea() const override
{
return M_PI * radius * radius;
}

inline double getPerimeter() const override
{
return 2 * M_PI * radius;
}

inline std::string toString() const override
{
return "Circle(radius=" + std::to_string(radius) + ")";
}

private:
double radius;
};

class Triangle : public Shape
{
public:
Triangle(double height, double base) : height{height}, base{base}, Shape(3) {}

inline ShapeType getType() const override
{
return ShapeType::Triangle;
}

inline double getArea() const override
{
return 0.5 * height * base;
}

inline double getPerimeter() const override
{
return base + 2 * std::sqrt(height * height + (base * base) / 4);
}

inline std::string toString() const override
{
return "Triangle(height=" + std::to_string(height) + ", base=" + std::to_string(base) + ")";
}

private:
double height;
double base;
};
54 changes: 54 additions & 0 deletions exercises-cpp/mohammed_almakki/ex02_oo_basics/src/ShapeSorter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include "ShapeSorter.h"

void ShapeSorter::filterAndPrintByPredicate(const std::vector<std::shared_ptr<Shape>> &shapes, const ShapeFilterPredicate &predicate) const
{
for_each(
shapes.begin(), shapes.end(), [predicate](auto shape)
{
if(predicate(shape)) {
std::cout << shape->toString() << " ";
} });
std::cout << "\n";
}

void ShapeSorter::printShapesWithType(const ShapeType &type) const
{
filterAndPrintByPredicate(shapes, [type](const std::shared_ptr<Shape> &shape)
{ return shape->getType() == type; });
}

void ShapeSorter::printShapesWithSides(const int &sidesCount) const
{
filterAndPrintByPredicate(shapes, [sidesCount](const std::shared_ptr<Shape> &shape)
{ return shape->getSidesCount() == sidesCount; });
}

void ShapeSorter::printOrderedShapesByArea() const
{
auto sortedShapes = shapes;

std::sort(
sortedShapes.begin(), sortedShapes.end(),
[](auto left, auto right)
{
return left->getArea() > right->getArea();
});

filterAndPrintByPredicate(sortedShapes, [](const std::shared_ptr<Shape> &shape)
{ return true; });
}

void ShapeSorter::printOrderedShapesByPerimeter() const
{
auto sortedShapes = shapes;

std::sort(
sortedShapes.begin(), sortedShapes.end(),
[](auto left, auto right)
{
return left->getPerimeter() > right->getPerimeter();
});

filterAndPrintByPredicate(sortedShapes, [](const std::shared_ptr<Shape> &shape)
{ return true; });
}
Loading