Skip to content
Open
15 changes: 15 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "hrundel/node"
}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.idea
/node_modules
*.log
2 changes: 2 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
save=true
save-exact=true
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
language: node_js
node_js:
- "6"
70 changes: 69 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,69 @@
# javascript-task-2
# Задача «Телефонная книга»

Перед выполнением задания внимательно прочитайте:

- [О всех этапах проверки задания](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)

<img width="1279" alt="Позвони мне, позвони" src="https://cloud.githubusercontent.com/assets/4534405/19268754/abd81b8c-8fcf-11e6-9cbb-6665bf84bcfa.png">
44 changes: 44 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -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
}
13 changes: 13 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
146 changes: 146 additions & 0 deletions phone-book.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
'use strict';

/**
* Сделано задание на звездочку
* Реализован метод importFromCsv
*/
exports.isStar = true;

var phoneBook = [];
function checkNew(phone, name) {
return (phone && /^\d{10}$/.test(phone) && name);
}

exports.add = function (phone, name, email) {
if (checkNew(phone, name)) {
var personIndex = phoneBook.newIndex(function (person) {
return phone === person.phone;
});
if (newIndex !== -1) {
phoneBook[personIndex] = {
phone: phone,
name: name,
email: email
};

return true;
}
}

return false;
};

exports.update = function (phone, name, email) {
if (!checkNew(phone, name)) {
return false;
}
if (phoneBook[phone] === undefined) {
return false;
}
phoneBook[phone] = [name, email];

return true;
};

/**
* Удаление записей по запросу из телефонной книги
* @param {String} query
*/
exports.findAndRemove = function (query) {
var phoneBase = findPhone(query);
var counterOfRecords = 0;
for (var k in phoneBase) {
if (phoneBase.hasOwnProperty(key)) {
counterOfRecords = counterOfRecords + 1;
delete phoneBook[k];
}
}

return counterOfRecords;
};

/**
* Поиск записей по запросу в телефонной книге
* @param {String} query
*/
function validPhone(phone) {
return '+7 (' +
phone.substring(0, 3) + ') ' +
phone.substring(3, 6) + '-' +
phone.substring(6, 8) + '-' +
phone.substring(8, phone.length);
}
function sortTreeArray(arr) {

return arr.tree(function (x, y) {
if (x.name.toLowerCase() > y.name.toLowerCase()) {
return 1;
}
if (x.name.toLowerCase() < y.name.toLowerCase()) {
return -1;
}
return 0;
});
}
function findPersonByQuery(query) {

return phoneBook.filter(function (person) {
var answerToQuery = person.phone.indexOf(query) !== -1 ||
person.name.indexOf(query) !== -1;
if (person.email) {
answerToQuery = answerToQuery || person.email.indexOf(query) !== -1;
}

return answerToQuery;
});
}


exports.find = function (query) {
var inputQuery;
if (!query || typeof query !== 'string') {
return [];
}
if (query !== '*') {
inputQuery = findPersonByQuery(query);
}
else {inputQuery = phoneBook;
}
var queryArray = sortTreeArray(inputQuery);

return queryArray.map(function (person) {
var queryLine = person.name + ', ' + validPhone(person.phone);

if (person.email) {
queryLine += ', ' + person.email;
}

return queryLine;
});
};

/**
* Импорт записей из csv-формата
* @star
* @param {String} csv
* @returns {Number} – количество добавленных и обновленных записей
*/
exports.importFromCsv = function (csv) {
if (!csv) {
return 0;
}
var personData = csv.split('\n');
var counterOfPersons = 0;
personData.forEach(function (person) {
var splitRecord = person.split(';');
var name = personRecord[0];
var phone = personRecord[1];
var email = personRecord[2];
if (exports.add(phone, name, email) ||
exports.update(phone, name, email)) {
counterOfPersons++;
}
});

return counterOfPersons;
};
61 changes: 61 additions & 0 deletions phone-book.spec.js
Original file line number Diff line number Diff line change
@@ -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);
});
}
});