Skip to content

Commit

Permalink
re organize project structure and include functionality to allow for …
Browse files Browse the repository at this point in the history
…tests, cleaner project structure and __main__.py included for console usage.

new quick sort algorithm added.
unit tests for sorting algorithms and parser tests.
extra setup.py functionality added and name renamed to 'sorter'
small readme changes.
  • Loading branch information
Brett committed Apr 14, 2018
1 parent 99c66ba commit f350548
Show file tree
Hide file tree
Showing 8 changed files with 250 additions and 119 deletions.
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,13 @@ python -m sorter -g 100 -s bubble
- Be careful when using the **Bogo** sorting algorithm, it shuffles
an array and checks it sorted iteratively, larger data sets will take a long time (forever).

```python sorter.py -h``` can be used to view the available arguments possible.
```python -m sorter -h``` can be used to view the available arguments possible.

### Installing

- Clone repository locally
- ```python setup.py build```
- ```python setup.py install```
- Access module from console with ```python -m sorter```
- Clone repository locally.
- ```python setup.py install```.
- Access module from console with ```python -m sorter```.

## Authors

Expand Down
19 changes: 11 additions & 8 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from setuptools import setup
from setuptools import setup, find_packages

setup(
name='py-custom-sorters',
version='0.1.1',
url='https://github.com/becurrie/py-custom-sorters',
license='MIT',
author='becurrie',
name='sorter',
author_email='[email protected]',
description='Sort Integers in the console.',
py_modules=['sorter'],
author='becurrie',
version='0.1.2',
description='sort integers with different sorting algorithms.',
packages=find_packages(),
py_modules=['sorter.sorter'],
entry_points={'console_scripts': ['sorter = sorter.sorter:main']},
license='MIT',
url='https://github.com/becurrie/py-custom-sorters',
keywords=['sorting', 'algorithm', 'console', 'application'],
)
106 changes: 0 additions & 106 deletions sorter.py

This file was deleted.

File renamed without changes.
7 changes: 7 additions & 0 deletions sorter/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import sys

from sorter.sorter import main

if __name__ == "__main__":
# Allow import run through __name__ = __main__ idiom.
main(sys.argv)
115 changes: 115 additions & 0 deletions sorter/sorter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import argparse
import os
import sys
import timeit

from sorter.sorts import *


def parse_args(args):
"""Create the parser and add all required arguments before parsing
user input and returning the parser.parse_args() results.
"""
parser = argparse.ArgumentParser(description='sort some integers.')

group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-i', '--integers', type=int, nargs='+',
help='integer(s) being sorted.')
group.add_argument('-g', '--generate', type=int,
help='generate a random list of integers to sort.')
parser.add_argument('-s', '--sort', type=str, required=True,
choices=['bubble', 'bogo', 'merge', 'selection', 'quick'],
help='type of sort being performed.')

return parser.parse_args(args)


def print_results(sort_type, original_list, sorted_list, time):
"""Print an original list, sorted list and time required to sort the original list."""
print('\tSort Type: [%s]' % str.upper(sort_type))
print('\tOriginal List:')
for original_chunk in print_list_chunks(original_list, 20):
print('\t' + str(original_chunk)[1:-1])

print('\n\tSorted List:')
for sorted_chunk in print_list_chunks(sorted_list, 20):
print('\t' + str(sorted_chunk)[1:-1])

# Print time required to sort list.
print('\tTime(seconds): %s' % time)


def print_list_chunks(integer_list, n):
"""Simple helper method to print out a list in chunks."""
for i in range(0, len(integer_list), n):
yield integer_list[i:i + n]


def generate_integer_list(size):
"""Generate a list of Integers between specified size value."""
integer_list = list()
for i in range(0, size):
integer_list.append(random.randint(0, 1000))

return integer_list


def sort(sort_type, integers):
"""Sort a list of integers based on the type of sort specified."""
if sort_type == 'bubble':
initial = timeit.default_timer()
sorted_list = bubble_sort(integers)
final = timeit.default_timer()
print_results(sort_type, integers, sorted_list, final - initial)

elif sort_type == 'bogo':
initial = timeit.default_timer()
sorted_list = bogo_sort(integers)
final = timeit.default_timer()
print_results(sort_type, integers, sorted_list, final - initial)

elif sort_type == 'selection':
initial = timeit.default_timer()
sorted_list = selection_sort(integers)
final = timeit.default_timer()
print_results(sort_type, integers, sorted_list, final - initial)

elif sort_type == 'merge':
initial = timeit.default_timer()
original = list(integers)
sorted_list = merge_sort(integers)
final = timeit.default_timer()
print_results(sort_type, original, sorted_list, final - initial)

elif sort_type == 'quick':
initial = timeit.default_timer()
original = list(integers)
quick_sort(integers)
final = timeit.default_timer()

# Slightly different print call, quick_sort sorts in place.
# A new list isn't made, args.integers becomes sorted.
print_results(sort_type, original, integers, final - initial)


def main(args):
"""Main method. build arguments, clear console and parse arguments"""
args = parse_args(args[1:])

# Clear system terminal based on operating system name.
if os.name == 'posix':
os.system('clear')
elif os.name == 'nt':
os.system('cls')

# Check for generate argument specified and create random list.
if args.generate:
args.integers = generate_integer_list(args.generate)

# Main sort() method call from main.
sort(args.sort, args.integers)


if __name__ == "__main__":
# Allow import run through __name__ = __main__ idiom.
main(sys.argv)
50 changes: 50 additions & 0 deletions sorts.py → sorter/sorts.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,53 @@ def bogo_sort(integers):
is_sorted = True

return integers_clone


def quick_sort(integers):
"""Perform a quick sort on a list of integers, selecting a pivot
point, partition all elements into a first and second part while
looping so all elements < pivot are in first part, any elements
> then pivot are in seconds part, recursively sort both half's
and combine.
"""
quick_sort_helper(integers, 0, len(integers) - 1)


def quick_sort_helper(integers, first, last):
"""Small helper method for calling and recursively finding
pivot/split points.
"""
if first < last:
split = quick_sort_partition(integers, first, last)

quick_sort_helper(integers, first, split - 1)
quick_sort_helper(integers, split + 1, last)


def quick_sort_partition(integers, first, last):
"""Generate a correct partition point for the given list of integers."""
pivot_value = integers[first]

left = first + 1
right = last

done = False
while not done:
while left <= right and integers[left] <= pivot_value:
left += 1

while integers[right] >= pivot_value and right >= left:
right -= 1

if right < left:
done = True
else:
temp = integers[left]
integers[left] = integers[right]
integers[right] = temp

temp = integers[first]
integers[first] = integers[right]
integers[right] = temp

return right
63 changes: 63 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import unittest

from sorter.sorts import *

from sorter.sorter import parse_args


class TestParsing(unittest.TestCase):

def test_integers(self):
"""Test the optional one of: integers argument."""
parser = parse_args(['-i', '1', '3', '6', '9', '-s', 'bubble'])
self.assertTrue(parser.integers)

def test_generate(self):
"""Test the optional one of: generate argument."""
parser = parse_args(['-g', '100', '-s', 'bubble'])
self.assertTrue(parser.generate)

def test_sort(self):
"""Test the required sort argument."""
parser = parse_args(['-g', '10', '-s', 'bubble'])
self.assertTrue(parser.sort)


class TestSorts(unittest.TestCase):

def setUp(self):
"""Simple setup function to create the actual/expected
lists that each sort method should produce if working as
intended.
"""
self.actual = [5, 7, 4, 9, 1, 2]
self.expected = [1, 2, 4, 5, 7, 9]

def test_bubble(self):
"""Test the bubble_sort function."""
integers = bubble_sort(self.actual)
self.assertEqual(self.expected, integers)

def test_selection(self):
"""Test the selection_sort function."""
integers = bubble_sort(self.actual)
self.assertEqual(self.expected, integers)

def test_bogo(self):
"""Test the bogo_sort function."""
integers = bogo_sort(self.actual)
self.assertEqual(self.expected, integers)

def test_merge(self):
"""Test the merge_sort function."""
integers = merge_sort(self.actual)
self.assertEqual(self.expected, integers)

def test_quick(self):
"""Test the quick_sort function. This test is slightly different
because the quick_sort method sorts in place and doesn't return
a new sorted list.
"""
clone = self.actual
quick_sort(clone)
self.assertEqual(self.expected, clone)

0 comments on commit f350548

Please sign in to comment.