diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..936d024 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_size = 4 +indent_style = space +trim_trailing_whitespace = true + +[*.json] +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..db814bf --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "hrundel/node" +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d48db5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea +/node_modules +*.log diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..f5357d5 --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +save=true +save-exact=true diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..4909f83 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,3 @@ +language: node_js +node_js: + - "6" diff --git a/README.md b/README.md index 07fbe88..44a59bf 100644 --- a/README.md +++ b/README.md @@ -1 +1,68 @@ -# javascript-task-2 \ No newline at end of file +# Задача «Телефонная книга» + +Перед выполнением задания внимательно прочитайте: + +- [О всех этапах проверки задания](https://github.com/urfu-2016/guides/blob/master/workflow/extra.md) +- [Как отправить пулл](https://github.com/urfu-2016/guides/blob/master/workflow/pull.md) +- [Как пройти тесты](https://github.com/urfu-2016/guides/blob/master/workflow/test.md) +- Правила оформления [javascript](https://github.com/urfu-2016/guides/blob/master/codestyle/js.md), [HTML](https://github.com/urfu-2016/guides/blob/master/codestyle/html.md) и [CSS](https://github.com/urfu-2016/guides/blob/master/codestyle/css.md) кода +- [Лекцию «Типы данных»](https://urfu-2016.github.io/javascript-slides/02-types/#/) + + +## Основное задание + +> Мы очень хотим, чтобы код вы написали сами, а не пользовались внешними библиотеками. + +Как известно, каждый уважающий себя разработчик должен в жизни сделать три вещи: +- [x] посадить DOM дерево +- [x] построить абстракцию +- [ ] ~~вырастить~~ написать телефонную книгу + +Предлагаем вам пройти легкий путь становления уважающего себя разработчика и реализовать для скрипта телефонной книги __phone-book.js__ ряд необходимых методов. + +Метод __add__ для добавления записей: +* На вход принимает «Телефон», «Имя» и «Электронную почту» +* Возвращает true или false в зависимости от успеха опереации +* Телефоны принимаются **только** в формате 5556667788 (без кода) +* Не добавляет **уже существующую** запись + +Метод __update__ для обновления записей: +* На вход принимает «Телефон», «Имя» и «Электронную почту» +* Обновляет «Имя» и «Электронную почту» по заданному «Телефону» +* Возвращает true или false в зависимости от успеха опереации +* «Электронную почту» можно стереть (не передав последний параметр), а «Имя» – нет + +Метод __find__ для поиска записей: +* На вход принимает запрос в виде строки +* Ищет вхождение этой строки хотя бы в одно из полей «Телефон», «Имя» и «Электронную почту» +* Возвращает отсортированный по «Имени» массив строк в формате `name, phone, email` +* «Имя» и «Электронную почту» выводит как есть, а «Телефон» в формате `+7 (555) 666-77-88` +* Пустой запрос не должен ничего находить +* Запрос «*» находит все записи + +Метод __findAndRemove__ для удаления записей: +* На вход принимает запрос в виде строки +* Находит (смотри __find__) и удаляет все найденные записи +* Возвращает число удаленных записей + +В файле _index.js_ вы можете найти примеры использования получившегося скриптика. + +## Дополнительное задание + +> Перед выполнением внимательно прочитайте [про особенности](https://github.com/urfu-2016/guides/blob/master/workflow/extra.md) + +По одной добавлять записи в книгу не очень удобно, поэтому будет здорово, если вы добавите в решение импорт данных из csv. Для этого реализуйте код метода __importFromCsv__. + +На вход метод принимает строку в формате csv. Если запись в телефонной книги уже есть – обновляет/дополняет её данными из csv строки. На выходе метод возвращает одно число добавленных/обновленных записей. + +Пример работы этого метода вы может отыскать в _index.js_ и в тестах. + +## Полезные ссылки + +- [Знакомимся с массивами](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Array) +- [Пытаемся знакомиться с регулярными выражениями](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/RegExp) +- [Перебираем ключи объектов](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Object/keys) +- [Метод indexOf для строк](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf) +- [Метод slice для строк](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String/slice) + +Позвони мне, позвони diff --git a/index.js b/index.js new file mode 100644 index 0000000..9b259f5 --- /dev/null +++ b/index.js @@ -0,0 +1,44 @@ +'use strict'; + +var phoneBook = require('./phone-book'); + +// Эти записи добавятся, вернется true +phoneBook.add('5554440044', 'Григорий', 'grisha@example.com'); +phoneBook.add('5552220022', 'Борис', 'boris@example.com'); +phoneBook.add('5551110011', 'Алекс'); +phoneBook.add('5553330033', 'Валерий', 'valera@example.com'); + +// Эти запись не добавятся +phoneBook.add('3330033', 'Неизвестный', 'unknown@example.com'); +phoneBook.add('5551110011', 'Алексей'); +phoneBook.add('5555550055'); + +// Обновление +phoneBook.update('5551110011', 'Алексей', 'alex@example.com'); +phoneBook.update('5553330033', 'Валерий'); + +// В следующих примерах вернутся все записи +console.info(phoneBook.find('*')); +console.info(phoneBook.find('555')); +// Вывод будет следующий +// [ +// 'Алексей, +7 (555) 111-00-11, alex@example.com', +// 'Борис, +7 (555) 222-00-22, boris@example.com', +// 'Валерий, +7 (555) 333-00-33', +// 'Григорий, +7 (555) 444-00-44, grisha@example.com' +// ] + +// Удаление +phoneBook.findAndRemove('@'); // returns 3 + +if (phoneBook.isStar) { + // Импортируем из csv + var csv = [ + 'Борис;5552220022;boris@example.com', + 'Григорий;5554440044;grisha@example.com', + 'Алексей;5551110011;alex@example.com', + 'Валерий;5553330033;valera@example.com', + 'Неизвестный;3330033;unknown@example.com' + ].join('\n'); + phoneBook.importFromCsv(csv); // returns 4 +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1400772 --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "private": true, + "main": "phone-book.js", + "scripts": { + "lint": "eslint .", + "test": "eslint . && mocha *.spec.js" + }, + "dependencies": { + "eslint": "3.7.0", + "eslint-config-hrundel": "latest", + "mocha": "3.1.0" + } +} diff --git a/phone-book.js b/phone-book.js new file mode 100644 index 0000000..cb276ff --- /dev/null +++ b/phone-book.js @@ -0,0 +1,247 @@ +'use strict'; + +/** + * Сделано задание на звездочку. + * Реализован метод importFromCsv + */ +exports.isStar = false; + +/** + * Телефонная книга + */ +var phoneBook = []; +var NAME = 'Name'; +var PHONE = 'Phone'; +var EMAIL = 'Email'; + +/** + * creat correct phone. + * @param {String} phone + * @returns {String} отредактированный номер + */ +function correctPhone(phone) { + + return '+7 (555) ' + phone.substr(3, 3) + '-' + phone.substr(6, 2) + '-' + phone.substr(8, 2); +} + +/** + * @param {Number} i + * @param {String} query + * @returns {Boolean} + */ +function index(i, query) { + if ((phoneBook[i][NAME].indexOf(query) >= 0) || + (phoneBook[i][PHONE].indexOf(query) >= 0) || + (phoneBook[i][EMAIL].indexOf(query) >= 0)) { + + return true; + } + + return false; +} + +/** + * swap i and last elem. + * @param {Number} num + * @returns {void} + */ +function swap(num) { + var data = phoneBook[num]; + phoneBook[num] = phoneBook[phoneBook.length - 1]; + phoneBook[phoneBook.length - 1] = data; +} + +/** + * the existence check. + * @param {String} phone + * @returns {Boolean} нашли нет + */ +function findForAdd(phone) { + for (var i = 0; i < phoneBook.length; i++) { + if (phoneBook[i][PHONE] === phone) { + + return true; + } + } + + return false; +} + +/** + * проверяет корректность. + * @param {String} data + * @returns {Boolean} нашли нет + */ +function correctData(data) { + try { + if (data.length === 0) { + + return false; + } + } catch (err) { + + return false; + } + + return true; +} +function correctUpdate(phone, name, email) { + if (!(/^555\d\d\d\d\d\d\d$/.test(phone)) || (name === undefined) || (email === null)) { + + return false; + } + if (name === null) { + + return false; + } + + return true; +} + +function getAllData() { + var result = []; + for (var i = 0; i < phoneBook.length; i++) { + result.push(correctStr(phoneBook[i])); + } + + return result.sort(); +} + +/** + * Добавление записи в телефонную книгу + * @param {String} str + * @returns {String} строка + */ +function correctStr(str) { + if (str[EMAIL] === '') { + + return str[NAME] + ', ' + correctPhone(str[PHONE]); + } + + return str[NAME] + ', ' + correctPhone(str[PHONE]) + ', ' + str[EMAIL]; +} + +/** + * Добавление записи в телефонную книгу + * @param {String} phone + * @param {String} name + * @param {String} email + * @returns {Boolean} добавили нет + */ +exports.add = function (phone, name, email) { + if (!correctUpdate(phone, name, email)) { + + return false; + } + if (email === undefined) { + email = ''; + } + if (!(correctData(name))) { + + return false; + } + if (findForAdd(phone, name, email)) { + + return false; + } + phoneBook.push({ 'Phone': phone, 'Name': name, 'Email': email }); + + return true; +}; + +/** + * Удаление записей по запросу из телефонной книги + * @param {String} query + * @returns {Number} колличество удаленных строк + */ +exports.findAndRemove = function (query) { + var result = 0; + if (query === '*') { + result = phoneBook.length; + phoneBook = []; + } + if (!correctData(query)) { + + return 0; + } + for (var i = 0; i < phoneBook.length; i++) { + if (index(i, query)) { + result++; + swap(i); + i = i - 1; + phoneBook.pop(); + } + } + + return result; +}; + +/** + * Обновление записи в телефонной книге + * @param {String} phone + * @param {String} name + * @param {String} email + * @returns {Boolean} обновили или нет + */ +exports.update = function (phone, name, email) { + if (!correctUpdate(phone, name, email)) { + + return false; + + } + if (email === undefined) { + email = ''; + } + var result = 0; + for (var i = 0; i < phoneBook.length; i++) { + if (phoneBook[i][PHONE] === phone) { + phoneBook[i][NAME] = name; + phoneBook[i][EMAIL] = email; + result++; + } + } + if (result < 0) { + + return false; + } + + return true; +}; + +/** + * Поиск записей по запросу в телефонной книге + * @param {String} query + * @returns {Array} массив строк + */ +exports.find = function (query) { + var result = []; + if (query === '*') { + + return getAllData(phoneBook); + } + if (!correctData(query)) { + + return []; + } + for (var i = 0; i < phoneBook.length; i++) { + if (index(i, query)) { + result.push(correctStr(phoneBook[i])); + } + } + + return result.sort(); +}; + +/** + * Импорт записей из csv-формата + * @star + * @param {String} csv + * @returns {Number} – количество добавленных и обновленных записей + */ +exports.importFromCsv = function (csv) { + // Парсим csv + // Добавляем в телефонную книгу + // Либо обновляем, если запись с таким телефоном уже существует + + return csv.split('\n').length; +}; diff --git a/phone-book.spec.js b/phone-book.spec.js new file mode 100644 index 0000000..1b62a1a --- /dev/null +++ b/phone-book.spec.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +'use strict'; + +var assert = require('assert'); + +var phoneBook = require('./phone-book'); + +describe('phone-book', function () { + it('должен добавлять записи', function () { + assert.ok(phoneBook.add('5554440044', 'Григорий', 'grisha@example.com')); + assert.ok(phoneBook.add('5552220022', 'Борис', 'boris@example.com')); + assert.ok(phoneBook.add('5551110011', 'Алекс')); + assert.ok(phoneBook.add('5553330033', 'Валерий', 'valera@example.com')); + }); + + it('не должен добавлять неправильные записи', function () { + assert.ok(!phoneBook.add('3330033', 'Неизвестный', 'unknown@example.com')); + assert.ok(!phoneBook.add('5551110011', 'Алексей')); + assert.ok(!phoneBook.add('5555550055')); + }); + + it('должен обновлять существующие записи', function () { + assert.ok(phoneBook.update('5551110011', 'Алексей', 'alex@example.com')); + assert.ok(phoneBook.update('5553330033', 'Валерий')); + }); + + it('должен искать все записи по запросу "*"', function () { + assert.deepStrictEqual(phoneBook.find('*'), [ + 'Алексей, +7 (555) 111-00-11, alex@example.com', + 'Борис, +7 (555) 222-00-22, boris@example.com', + 'Валерий, +7 (555) 333-00-33', + 'Григорий, +7 (555) 444-00-44, grisha@example.com' + ]); + }); + + it('должен искать все записи по запросу "555"', function () { + assert.deepStrictEqual(phoneBook.find('555'), [ + 'Алексей, +7 (555) 111-00-11, alex@example.com', + 'Борис, +7 (555) 222-00-22, boris@example.com', + 'Валерий, +7 (555) 333-00-33', + 'Григорий, +7 (555) 444-00-44, grisha@example.com' + ]); + }); + + it('должен удалять элементы из телефонной книги', function () { + assert.strictEqual(phoneBook.findAndRemove('@'), 3); + }); + + if (phoneBook.isStar) { + it('должен экспортировать из cvs', function () { + var csv = [ + 'Борис;5552220022;boris@example.com', + 'Григорий;5554440044;grisha@example.com', + 'Алексей;5551110011;alex@example.com', + 'Валерий;5553330033;valera@example.com', + 'Неизвестный;3330033;unknown@example.com' + ].join('\n'); + assert.strictEqual(phoneBook.importFromCsv(csv), 4); + }); + } +});