Merge branch 'linked_list' into lab_0
Merge branch 'linked_list' into lab_1 Добавлена реализация связного списка для лабораторной работы №1# the commit.
This commit is contained in:
commit
f4666dae8b
2
skorohodovsa/.gitignore
vendored
Normal file
2
skorohodovsa/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
/.ruff_check
|
||||||
|
/.vscode
|
||||||
121
skorohodovsa/task_1/linked_list.py
Normal file
121
skorohodovsa/task_1/linked_list.py
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
def create_node(name: str, phone: str, next: dict = None):
|
||||||
|
return {"name": name, "phone": phone, "next": next}
|
||||||
|
|
||||||
|
|
||||||
|
def create_linked_list(data: list[dict]) -> dict:
|
||||||
|
"""Создание связного списка по массиву словарей.
|
||||||
|
|
||||||
|
:param data: Список словарей с параметрами: {'name': str, 'phone': str, next: dict | None}
|
||||||
|
:type data: list[dict]
|
||||||
|
:raises ValueError: Ошибка при подаче на вход пустого списка
|
||||||
|
:return: Связный список оформленный по примеру: {'name': str, 'phone': str, next: dict | None}
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
if data is None or len(data) == 0:
|
||||||
|
raise ValueError("Список пустой!")
|
||||||
|
|
||||||
|
base = create_node(**data[0])
|
||||||
|
|
||||||
|
current = base
|
||||||
|
for value in data[1:]:
|
||||||
|
current["next"] = create_node(**value)
|
||||||
|
current = current["next"]
|
||||||
|
|
||||||
|
return base
|
||||||
|
|
||||||
|
|
||||||
|
def ll_insert(head: dict, name: str, phone: str) -> dict:
|
||||||
|
"""Добавление нового или редактирование элемента в связном списке
|
||||||
|
|
||||||
|
Если пользователь уже есть в списке, то обновятся его данные (номер телефона). В случае если
|
||||||
|
данных нет, то они добавляются в конец.
|
||||||
|
|
||||||
|
:param head: Список словарей с параметрами: {'name': str, 'phone': str, next: dict | None}
|
||||||
|
:type head: dict
|
||||||
|
:param name: Имя пользователя (не должно повторятся с имеющимися)
|
||||||
|
:type name: str
|
||||||
|
:param phone: Номер телефона пользователя (не должно повторятся с имеющимися)
|
||||||
|
:type phone: str
|
||||||
|
:raises ValueError: Ошибка при подаче на вход пустого списка
|
||||||
|
:return: Возвращает связный список с обновленными данными
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
if head is None:
|
||||||
|
raise ValueError("Словарь пустой!")
|
||||||
|
|
||||||
|
current = head
|
||||||
|
while current["next"] is not None:
|
||||||
|
if current.get("name") == name or current.get("phone") == phone:
|
||||||
|
current["name"] = name
|
||||||
|
current["phone"] = phone
|
||||||
|
break
|
||||||
|
current = current.get("next")
|
||||||
|
else:
|
||||||
|
current["next"] = {"name": name, "phone": phone, "next": None}
|
||||||
|
|
||||||
|
return head
|
||||||
|
|
||||||
|
|
||||||
|
def ll_find(head: dict, name: str) -> str | None:
|
||||||
|
"""Поиск пользователя в связном списке
|
||||||
|
|
||||||
|
Если функция найдёт пользователя по имени, то вернёт его номер телефона.
|
||||||
|
В противном случае будет возвращено значение None.
|
||||||
|
|
||||||
|
В случае повторяющихся имен в списке, выведется выше стоящие
|
||||||
|
|
||||||
|
:param head: Список словарей с параметрами: {'name': str, 'phone': str, next: dict | None}
|
||||||
|
:type head: dict
|
||||||
|
:param name: Имя пользователя
|
||||||
|
:type name: str
|
||||||
|
:raises ValueError: Ошибка при подаче на вход пустого списка
|
||||||
|
:return: Возвращает номер телефона найденного пользователя, иначе None
|
||||||
|
:rtype: str | None
|
||||||
|
"""
|
||||||
|
if head is None:
|
||||||
|
raise ValueError("Словарь пустой!")
|
||||||
|
|
||||||
|
current = head
|
||||||
|
while current is not None:
|
||||||
|
if current["name"] == name:
|
||||||
|
return current["phone"]
|
||||||
|
current = current["next"]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def ll_delete(head: dict, name: str) -> dict:
|
||||||
|
"""Удаление пользователя из связного списка
|
||||||
|
|
||||||
|
:param head: Список словарей с параметрами: {'name': str, 'phone': str, next: dict | None}
|
||||||
|
:type head: dict
|
||||||
|
:param name: Имя пользователя
|
||||||
|
:type name: str
|
||||||
|
:raises ValueError: Ошибка при подаче на вход пустого списка
|
||||||
|
:return: Возвращает связный список с обновленными данными
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
if head is None:
|
||||||
|
raise ValueError("Словарь пустой!")
|
||||||
|
|
||||||
|
if head.get("name") == name:
|
||||||
|
head = head.get("next")
|
||||||
|
return head
|
||||||
|
|
||||||
|
current = head
|
||||||
|
while current.get("next") is not None:
|
||||||
|
if current.get("next").get("name") == name:
|
||||||
|
current["next"] = current.get("next").get("next")
|
||||||
|
return head
|
||||||
|
current = current.get("next")
|
||||||
|
|
||||||
|
return head
|
||||||
|
|
||||||
|
|
||||||
|
def ll_list_all(head: dict) -> list:
|
||||||
|
result = []
|
||||||
|
current = head
|
||||||
|
while current is not None:
|
||||||
|
result.append({"name": current.get("name"), "phone": current.get("phone")})
|
||||||
|
current = current.get("next")
|
||||||
|
return result
|
||||||
|
|
||||||
128
skorohodovsa/task_1/test/test_task_1.py
Normal file
128
skorohodovsa/task_1/test/test_task_1.py
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
import pytest
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import copy
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||||
|
|
||||||
|
from linked_list import create_linked_list, ll_find, ll_insert, ll_list_all, ll_delete
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def test_records():
|
||||||
|
return [
|
||||||
|
{"name": "Анна", "phone": "89123456789"},
|
||||||
|
{"name": "Михаил", "phone": "79223334455"},
|
||||||
|
{"name": "Елена", "phone": "4951234567"},
|
||||||
|
{"name": "Дмитрий", "phone": "9111112233"},
|
||||||
|
{"name": "Ольга", "phone": "81234567890"},
|
||||||
|
{"name": "Александр", "phone": "9219998877"},
|
||||||
|
{"name": "Татьяна", "phone": "4955556666"},
|
||||||
|
{"name": "Иван", "phone": "9034443322"},
|
||||||
|
{"name": "Наталья", "phone": "9167778899"},
|
||||||
|
{"name": "Павел", "phone": "9256665544"},
|
||||||
|
{"name": "Мария", "phone": "4953332211"},
|
||||||
|
{"name": "Андрей", "phone": "9264443322"},
|
||||||
|
{"name": "Екатерина", "phone": "8125554433"},
|
||||||
|
{"name": "Владимир", "phone": "9107778899"},
|
||||||
|
{"name": "Юлия", "phone": "4951112233"},
|
||||||
|
{"name": "Николай", "phone": "9215556677"},
|
||||||
|
{"name": "Светлана", "phone": "9164443322"},
|
||||||
|
{"name": "Артем", "phone": "9253334455"},
|
||||||
|
{"name": "Ксения", "phone": "4952223344"},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def linked_list(test_records):
|
||||||
|
return create_linked_list(test_records)
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_linked_list(test_records):
|
||||||
|
linked_list = create_linked_list(test_records)
|
||||||
|
assert linked_list is not None
|
||||||
|
|
||||||
|
temp = linked_list
|
||||||
|
index = 0
|
||||||
|
|
||||||
|
while temp.get("next") is not None:
|
||||||
|
assert temp.get("phone") == test_records[index].get("phone")
|
||||||
|
assert temp.get("name") == test_records[index].get("name")
|
||||||
|
|
||||||
|
temp = temp.get("next")
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_ll_find(linked_list):
|
||||||
|
assert linked_list is not None
|
||||||
|
|
||||||
|
test_list = [
|
||||||
|
{"name": "Анна", "phone": "89123456789"},
|
||||||
|
{"name": "Андрей", "phone": "9264443322"},
|
||||||
|
{"name": "Владимир", "phone": "9107778899"},
|
||||||
|
{"name": "Сергей", "phone": None},
|
||||||
|
{"name": "Ксения", "phone": "4952223344"},
|
||||||
|
]
|
||||||
|
|
||||||
|
for test in test_list:
|
||||||
|
assert ll_find(linked_list, test.get("name")) == test.get("phone")
|
||||||
|
|
||||||
|
|
||||||
|
def test_ll_insert_edit(linked_list, test_records):
|
||||||
|
assert linked_list is not None
|
||||||
|
|
||||||
|
test_list = [
|
||||||
|
{"name": "Анна", "phone": "89123456745"},
|
||||||
|
{"name": "Андрей", "phone": "926444332232"},
|
||||||
|
{"name": "Владимир", "phone": "9107778899"},
|
||||||
|
{"name": "Ксения", "phone": "4952223344"},
|
||||||
|
]
|
||||||
|
|
||||||
|
for test in test_list:
|
||||||
|
test_ll = copy.deepcopy(linked_list)
|
||||||
|
result_insert = ll_insert(test_ll, test.get("name"), test.get("phone"))
|
||||||
|
|
||||||
|
# Проверяем наличие изменения номера телефона
|
||||||
|
assert ll_find(result_insert, test.get("name")) == test.get("phone")
|
||||||
|
|
||||||
|
# Проверяем правильность места изменения
|
||||||
|
for i, value in enumerate(test_records):
|
||||||
|
if value.get("name") == test.get("name"):
|
||||||
|
assert ll_list_all(result_insert)[i].get("phone") == test.get("phone")
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def test_ll_insert_new(linked_list):
|
||||||
|
assert linked_list is not None
|
||||||
|
|
||||||
|
new_name = "Новый контакт"
|
||||||
|
new_phone = "99999999999"
|
||||||
|
|
||||||
|
test_ll = copy.deepcopy(linked_list)
|
||||||
|
|
||||||
|
result = ll_insert(test_ll, new_name, new_phone)
|
||||||
|
|
||||||
|
assert ll_find(result, new_name) == new_phone
|
||||||
|
|
||||||
|
# Проверяем, что новый элемент в конце
|
||||||
|
all_items = ll_list_all(result)
|
||||||
|
assert all_items[-1].get("name") == new_name
|
||||||
|
|
||||||
|
def test_ll_delete(linked_list, test_records):
|
||||||
|
assert linked_list is not None
|
||||||
|
|
||||||
|
tests = [
|
||||||
|
test_records[0],
|
||||||
|
test_records[1],
|
||||||
|
test_records[len(test_records) // 2],
|
||||||
|
test_records[-2],
|
||||||
|
test_records[-1],
|
||||||
|
{"name": "Сергей", "phone": "89290504426"},
|
||||||
|
]
|
||||||
|
|
||||||
|
for test in tests:
|
||||||
|
test_ll = copy.deepcopy(linked_list)
|
||||||
|
|
||||||
|
result_delete = ll_delete(test_ll, test.get('name'))
|
||||||
|
|
||||||
|
assert ll_find(result_delete, test.get('name')) is None
|
||||||
Loading…
Reference in New Issue
Block a user