Last active
September 16, 2018 17:57
-
-
Save codyfet/618d4f12c03cd0613225f43fa30b7d7b to your computer and use it in GitHub Desktop.
Решение задачи про query и операторы.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const friends = [ | |
{ | |
name: 'Сэм', | |
age: 29, | |
gender: 'Мужской', | |
email: 'luisazamora@example.com', | |
phone: '+7 (555) 505-3570', | |
favoriteFruit: 'Мороженое' | |
}, | |
{ | |
name: 'Эмили', | |
age: 30, | |
gender: 'Женский', | |
email: 'roachpugh@example.com', | |
phone: '+7 (555) 539-2625', | |
favoriteFruit: 'Яблоко' | |
}, | |
{ | |
name: 'Иван', | |
age: 32, | |
gender: 'Мужской', | |
email: 'ivan@example.com', | |
phone: '+7 (555) 539-2625', | |
favoriteFruit: 'Груша' | |
}, | |
{ | |
name: 'Мария', | |
age: 25, | |
gender: 'Женский', | |
email: 'masha@example.com', | |
phone: '+7 (555) 539-2625', | |
favoriteFruit: 'Тыква' | |
}, | |
{ | |
name: 'Сергей', | |
age: 19, | |
gender: 'Мужской', | |
email: 'sg@example.com', | |
phone: '+7 (555) 539-2625', | |
favoriteFruit: 'Котлета' | |
} | |
]; | |
const index = { | |
/** | |
* Выполняет запрос. | |
* | |
* @prop {Object[]} collection Исходная коллекция. Во время выполнения не изменяется. | |
* @prop {Function[]} operators Функции-операторы, выполняемые в запросе. | |
*/ | |
query: function (collection, ...operators) { | |
// Если операторов нет, то возвращаем клон исходного массива. | |
if (operators.length === 0) { | |
return JSON.parse(JSON.stringify(collection)); | |
} | |
// Складываем исходную коллекцию в this для доступа из методов (initialCollection). | |
// Создаём переменную, где будет храниться результат выполнения запроса (resultCollection). | |
this.initialCollection = collection; | |
this.resultCollection; | |
// Последовательно выполняем функции-операторы запроса. Промежуточные результаты складываем в resultCollection. | |
operators.forEach( | |
(operator) => this.resultCollection = operator.call() | |
); | |
// Распечатываем результат. | |
console.log('Результат выполнения запроса:'); | |
console.log(this.resultCollection); | |
}, | |
/** | |
* Возвращает функцию-оператор select, фильтруюущую объекты по заданным атрибутам. | |
* | |
* @param {string[]} attributes Атрибуты записи, которые попадают в выборку. | |
* @returns {Function} Функция-оператор select. | |
*/ | |
select: function (...attributes) { | |
return () => this.filterByAttributes(this.initialCollection, attributes); | |
}, | |
/** | |
* Возвращает функцию-оператор filterIn, фильтруюущую объекты исходной коллекции по значениям заданного атрибута. | |
* | |
* @param {string} attribute Атрибут, по значениям которого будет происходить фильтрация. | |
* @param {string[]} values Допустимые значения атрибута. | |
* @returns {Function} Функция-оператор filterIn. | |
*/ | |
filterIn: function (attribute, values) { | |
return () => { | |
// Фильтруем исходную коллекцию по переданным значениям. | |
const filtered = this.initialCollection.filter( | |
(item) => { | |
return values.indexOf(item[attribute]) !== -1 | |
} | |
); | |
// Оставляем только те атрибуты, которые сейчас лежат в результирующей коллекции (после всех селектов, выполненных ранее). | |
// Атрибуты (ключи) результирующей коллекции берём из первой записи результирующей коллекции. | |
return this.filterByAttributes(filtered, Object.keys(this.resultCollection[0])); | |
} | |
}, | |
/** | |
* Возвращает функцию-оператор sortBy, сортирующую коллекцию. | |
* Функция-оператор sortBy возвращает новую результирующую коллекцию. | |
* | |
* @param {string} attribute Атрибут, по значениями которого будет происходить фильтрация. | |
* @param {string[]} values Допустимые значения атрибута. | |
* @returns {Function} Функция-оператор filterIn. | |
*/ | |
sortBy: function (attribute, by) { | |
return () => { | |
// Функция-компаратор, описывающая алгоритм сортировки. | |
const compare = function compare(a, b) { | |
if (a[attribute] < b[attribute]) | |
return by === 'asc' ? -1 : 1; | |
if (a[attribute] > b[attribute]) | |
return by === 'asc' ? 1 : -1; | |
return 0; | |
} | |
// Сортируем коллекцию согласно функции-компаратору. Исходную коллекцию менять нельзя, поэтому делаем копию. | |
const sortred = Object.assign([], this.initialCollection).sort(compare); | |
// Оставляем только те атрибуты, которые сейчас лежат в результирующей коллекции (после всех селектов, выполненных ранее). | |
// Атрибуты (ключи) результирующей коллекции берём из первой записи результирующей коллекции. | |
return this.filterByAttributes(sortred, Object.keys(this.resultCollection[0])); | |
} | |
}, | |
/** | |
* Возвращает функцию-оператор format, форматирующую значение заданного атрибута согласно форматирующей функции. | |
* Функция-оператор format возвращает новую коллекцию, созданную на базе результирующей коллекции, но с отформатированными значениями. | |
* | |
* @param {string} attribute Атрибут, значение которого будет форматироваться. | |
* @param {Function} formatter Функция, форматирующая значение. | |
* @returns {Function} Функция-оператор format. | |
*/ | |
format: function (attribute, formatter) { | |
return () => this.resultCollection.map( | |
(item) => { | |
item[attribute] = formatter(item[attribute]); | |
return item; | |
} | |
); | |
}, | |
/** | |
* Возвращает функцию-оператор limit, ограничивающую количество записей в результирующей выборке. | |
* Функция-оператор format оставляет в результирующей коллекции количество записей равное number. | |
* | |
* @param {number} number Допустимое количество записей в выборке. | |
* @returns {Function} Функция-оператор limit. | |
*/ | |
limit: function (number) { | |
return () => this.resultCollection.slice(0, number); | |
}, | |
/** | |
* Фильтрует записи переданной коллекции. Оставляет толко атрибуты, переданные в attributes. | |
* | |
* @param {Object[]} collection Коллекция объектов. | |
* @returns {Object[]} Отфильтрованная по атрибутам коллекция. | |
*/ | |
filterByAttributes: function (collection, attributes) { | |
let mergedAttributes = attributes; | |
// Нам необходимо объединить все селекты, чтобы в выборку попали атрибуты из разных селектов. | |
// Для этого мы проверяем есть ли результирующая коллекция и если есть - объединяем attributes с теми атрибутами, | |
// которые уже есть в результате (т.к. там лежат результаты предыдущих селектов). | |
// Дубликаты удаляются, т.к. используется Set. | |
if (this.resultCollection) { | |
mergedAttributes = [...new Set( | |
attributes.concat(...Object.keys(this.resultCollection[0])) | |
)]; | |
} | |
return collection.map( | |
(item) => { | |
const filteredItem = {}; | |
mergedAttributes.forEach( | |
(attribute) => { | |
// Если в фильтруемой записи нет переданного атрибута, то игнорируем этот атрибут. | |
if (item.hasOwnProperty(attribute)) { | |
filteredItem[attribute] = item[attribute]; | |
} | |
} | |
); | |
return filteredItem; | |
} | |
); | |
} | |
} | |
index.query( | |
friends, | |
index.select('name', 'email', 'fdfd'), | |
index.select('name', 'gender'), | |
index.filterIn('favoriteFruit', ['Яблоко', 'Мороженое']), | |
index.sortBy('age', 'asc'), | |
index.format('name', function (value) { | |
return value[0]; | |
}), | |
index.limit(4) | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment