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..133405a 100644 --- a/README.md +++ b/README.md @@ -1 +1,67 @@ -# 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..9ac08ac --- /dev/null +++ b/phone-book.js @@ -0,0 +1,281 @@ +'use strict'; + +/** + * Сделано задание на звездочку + * Реализован метод importFromCsv + */ +exports.isStar = false; + +function checkQuery(query) { + + var typeQuery = typeof query !== 'string'; + var emptyQuery = query === '' || query === ' '; + var undefQuery = typeof query === 'undefined'; + + if (typeQuery || emptyQuery || undefQuery) { + + return false; + } + + return true; +} + +function isEmptyQuery(query) { + + return query === undefined; +} + +function checkPhone(phone) { + var reg = /^(\d{10})$/; + + return reg.test(phone); +} + +function valEmail(email) { + + if (email === '') { + + return true; + + } + + return email; +} + +function formatPhone(phone) { + + var result = '+7 ({1}) {2}-{3}-{4}' + .replace('{1}', phone.slice(0, 3)) + .replace('{2}', phone.slice(3, 6)) + .replace('{3}', phone.slice(6, 8)) + .replace('{4}', phone.slice(8, 10)); + + return result; + +} + +function concatString(name, phone, email) { + var result; + + if (email !== '') { + + result = '{1}, {2}, {3}' + .replace('{1}', name) + .replace('{2}', phone) + .replace('{3}', email); + } else { + + result = '{1}, {2}' + .replace('{1}', name) + .replace('{2}', phone); + } + + return result; + +} + +function findIndex(query, phone, name, email) { + + if (email === undefined) { + + return false; + + } + + if (phone.indexOf(query) !== -1 || name.indexOf(query) !== -1 || email.indexOf(query) !== -1) { + + return true; + } + + return false; +} + +/** + * Телефонная книга + */ +var phoneBook = []; +var uName; + +function checkEntry(phone) { + + var resultsEntry = true; + + phoneBook.forEach(function (object, index) { + + var entryPhone = phoneBook[index].number; + + if (phoneBook.length === 0) { + + return resultsEntry; + + } else if (entryPhone.indexOf(phone) !== -1) { + + resultsEntry = false; + } + + }); + + return resultsEntry; +} + +/** + * Добавление записи в телефонную книгу + * @param {String} phone + * @param {String} name + * @param {String} email + * @returns {Boolean} - true или false от успеха операции + */ +exports.add = function (phone, name, email) { + var bookObj = {}; + uName = isEmptyQuery(name) ? false : name; + var checkEmail = isEmptyQuery(email) ? '' : email; + + if (checkPhone(phone) && checkQuery(uName) && valEmail(checkEmail) && checkEntry(phone)) { + + bookObj = { + number: phone, + username: uName, + mail: checkEmail + }; + + phoneBook.push(bookObj); + + return true; + } + + return false; +}; + + +/** + * Обновление записи в телефонной книге + * @param {String} phone + * @param {String} name + * @param {String} email + * @returns {Boolean} - true или false от успеха операции + */ +exports.update = function (phone, name, email) { + var upPhone = ''; + var checkEmail = isEmptyQuery(email) ? '' : email; + var fixName = !checkQuery(name) ? false : name; + + if (checkPhone(phone) && valEmail(checkEmail)) { + + phoneBook.forEach(function (object, index) { + + upPhone = phoneBook[index].number; + + if (upPhone.indexOf(phone) !== -1) { + + phoneBook[index].username = fixName; + phoneBook[index].mail = checkEmail; + + } + + }); + + return true; + } + + return false; + +}; + +/** + * Удаление записей по запросу из телефонной книги + * @param {String} query + * @returns {Number} - Число удаленных записей + */ +exports.findAndRemove = function (query) { + var removed = []; + var counter = 0; + + if (checkQuery(query)) { + + if (query === '*') { + + var bookLength = phoneBook.slice().length; + + return bookLength; + } + + phoneBook.forEach(function (element, index) { + + var findPhone = phoneBook[index].number; + var findName = phoneBook[index].username; + var findEmail = phoneBook[index].mail; + + if (findIndex(query, findPhone, findName, findEmail)) { + + removed.push(index); + counter = removed.length; + } + + }); + } + + return counter; +}; + +/** + * Поиск записей по запросу в телефонной книге + * @param {String} query + * @returns {String} Результат поиска + */ +exports.find = function (query) { + var searchResult = []; + var convertedPhone = ''; + var concatResult = ''; + + if (checkQuery(query)) { + + if (query === '*') { + + phoneBook.forEach(function (object, index) { + var findName = phoneBook[index].username; + var findEmail = phoneBook[index].mail; + convertedPhone = formatPhone(phoneBook[index].number); + concatResult = concatString(findName, convertedPhone, findEmail); + + searchResult.push(concatResult); + + }); + + return searchResult.sort(); + + } + + phoneBook.forEach(function (object, index) { + var findPhone = phoneBook[index].number; + var findName = phoneBook[index].username; + var findEmail = phoneBook[index].mail; + + if (findIndex(query, findPhone, findName, findEmail)) { + + convertedPhone = formatPhone(findPhone); + concatResult = concatString(findName, convertedPhone, findEmail); + searchResult.push(concatResult); + + } + + }); + + return searchResult.sort(); + } + + return []; +}; + +/** + * Импорт записей из 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); + }); + } +});