Цель состоит в том, чтобы создать Массив из Объект s, структура которого [{ day: String, name: [String] }, …]
. Отметив, что повторяющиеся дней будут сгруппированы в один и тот же Объект , который день указывает на группировку.
Входные данные представляют собой массив чей формат [{ birth: String, name: String }, …]
. Во-первых, вы захотите извлечь значимый день месяца из строки даты, которая не отформатирована стандартным способом (например, "25.04.1988"
).
Чтобы преобразовать эту String Вы можете использовать регулярное выражение с параметром обратного вызова, который упорядочивает дату в формате ISO-8601 Date , который стандарт ECMA предусматривает, что Date
должен поддерживать в его строительстве. Также обратите внимание, что Date ожидает, что его ввод будет в UT C времени, т. Е. Он не знает о часовом поясе.
Такая конструкция может выглядеть как
const input = new Date('23.04.1988'.replace(/^(\d+)[^\d+](\d+)[^\d+](\d+)$/, (...backRefs) => {
return `${backRefs[3]}-${backRefs[2]}-${backRefs[1]}`;
}));
Здесь используется синтаксис ECMAScript (который Node.js реализует) для объявления литерала регулярного выражения: /RegularExpressionBody/RegularExpressionFlags
, где флаги пусты, а тело выражения ^(\d+)[^\d+](\d+)[^\d+](\d+)$
.
Это регулярное выражение не соответствует действительной дате, но вместо этого любая конструкция из трех серий цифр (\d+)
, разбитых нечислительными c символами [^\d+]
, что составляет целую строку . Затем он восстанавливает их, используя обратные ссылки в порядке 3-2-1
с разделительными чертами в соответствии с форматом ISO-8601 Date .
Это часть `${backRefs[3]}-${backRefs[2]}-${backRefs[1]}`
, который использует литералов шаблона , часто называемый неправильно строкой шаблона s.
Создание даты объекта из этого нового переменная const input
будет работать, но метод toString()
вернет String "Invalid Date"
, если он действительно недопустим. Если введенная дата не соответствует регулярному выражению, так что обратная ссылка индексирует группу захвата, которая не захватывает; тогда тип построенного Дата равен undefined
, поскольку он будет создан с недопустимыми входами. Этот конкретный результат может быть использован для агрегирования неверных дат рождения. Теперь, когда вы знаете, как правильно извлечь день, вы можете приступить к сортировке и группировке входных данных.
const input = [{
"birth" : "23.04.1988",
"name": "Tom Smith"
},
{
"birth": "15.04.2010",
"name": "Mini Jall"
},
{
"birth": "23.04.2001",
"name":"Michael Bird"
}];
Пусть input
будет вашим вводом Массив из Объект s.
Array Object предоставляет функцию с именем map
на его прототипе , которую вы можете используйте на input
, так как это Array . Спецификация Function Array.prototype.map(callbackFn[, thisArg])
. Он вызывается один раз для каждого элемента, который существует в Array , который является объектом , через который проходит . Возвращенное значение обратного вызова заменяет исходный объект во временном массиве , который возвращается map
после завершения. Другими словами, вы можете отобразить ваш Array в новую структуру, возвращая эту структуру из обратного вызова, используя свойства каждого элемента во время итерации. Обратите внимание, что аргумент thisArg
является контекстом, в котором вызывается map
Функция , и что если вы вызываете input.map
, то контекст наследуется как input
, и поэтому thisArg
только необязательно.
Такой вызов будет выглядеть как input.map(argumentsList)
, где argumentsList - это список, содержащий только обратный вызов Функция . Обратный вызов принимает до трех параметров: currentValue, currentInndex и объект, по которому осуществляется обход.
Таким образом, ваш обратный вызов должен принимать форму
(curr, idx, arr) => { return…; } // or
function (curr, idx, arr) { return…; }
В этом обратном вызове вы хотите преобразовать birth
параметр для day , поэтому, используя рассмотренную методологию, вы сделаете что-то вроде
let dateCB = ({ birth, name }, idx, arr) => {
const dateString = birth.replace(/^(\d+)[^\d+](\d+)[^\d+](\d+)$/, (...backRefs) => {
return `${backRefs[3]}-${backRefs[2]}-${backRefs[1]}`;
});
const date = new Date(dateString);
const retObj = { day: date.getDate(), month: date.getUTCMonth() + 1, name };
Object.defineProperty(retObj, 'month', { enumerable: false });
return retObj;
};
Мы добавим 1
к месяцу, потому что getUTCMonth
возвращает нулевой индексированный месяц года. Также мы определяем свойство month
как не перечисляемое, поскольку не хотим, чтобы оно отображалось в объекте результата. Обратите также внимание на то, что curr
из более ранней версии был деструктурирован в { birth, name }
и реструктурирован в { day: date.getDate(), month: date.getUTCMonth() + 1, name }
. Деструктурирующее присваивание позволяет вам уменьшить свойства Object на имена свойств, а в argumentsList оно объявляет эти свойства как переменные в области действия функции стрелки. На самом деле это сокращение для { birth: birth, name: name }
, так как они имеют тот же идентификатор, что и свойства входного объекта curr
. В этот момент у вас будет Array из Object s.
[
{
"day" :23,
"month": 4,
"name": "Tom Smith"
},
{
"day": 15,
"month": 4,
"name": "Mini Jall"
},
{
"day": 23,
"month": 4,
"name": "Michael Bird"
}
]
За исключением того, что свойства month
не будут перечисляемыми.
Вы хотите сопоставить эти Объект s так, чтобы String name
вместо этого Array
из всех name
родительских Object акций идентичные day
и month
свойства с другими Object членами Array .
Так что мы будем использовать Array.prototype.reduce
, который определен как Array.prototype.reduce(callbackfn[, initialValue])
. Обратный вызов Функция принимает до четырех параметров: previousValue, currentValue, currentIndex и просматриваемый объект. Если вы укажете initialValue
, то reduce
повторяется, начиная с первого элемента, а initialValue
предоставляется для обратного вызова как previousValue
. Однако, если вы пропустите initialValue
, то reduce
повторяется, начиная со второго элемента, предоставляя первый элемент как previousValue
. То, что вы возвращаете из обратного вызова, затем передается следующей итерации как previousValue
.
Мы также будем использовать Array.prototype.findIndex
, который определен как Array.prototype.findIndex(predicate[, thisArg ])
. Предикат Функция принимает до трех параметров и должен возвращать логический принудительный результат (например, 1
, 0
, true
, false
, undefined
и др. c). findIndex
вернет -1
, если ни один предикат не вернет true
, или вернет индекс, достигнутый им, когда это сделает первый предикат.
Мы можем использовать это, чтобы выяснить, содержит ли массив соответствующий day
и month
для итерации редуктора.
({ day: tDay, month: tMonth, name: tName }) => {
return tDay === … && tMonth === …;
}
Мы хотим создать новый вывод Array , и мы будем перебирать ввод, используя reduce
, создавая выводим как мы go. По этой причине, Reduce будет вызываться с пустым массивом в качестве необязательного второго аргумента
let reducer = (prev, { day, month, name }) => {
if (prev.length === 0) { /// this is the first iteration, where prev is empty
prev.push({ day, month, name: [name] });
} else { /// this is any other iteration, now we have to search `prev`
let where = prev.findIndex(({ day: tDay, month: tMonth, name: tName }) => {
return tDay === day && tMonth === month;
});
if (where !== -1) {
prev[where].name
.push(name);
} else {
prev.push({ day, month, name: [name] });
}
}
return prev;
}
И вызов выглядит как
input.map(dateCB)
.reduce(reducer, []);
Наконец, мы смотрим на функцию Array.prototype.sort
, определенную как Array.prototype.sort(comparefn)
. comparefn
Функция получает два аргумента, x
и y
. Работа comparefn
состоит в том, чтобы описать, как x
относится к y
. Сортировщик ожидает отрицательное число , возвращаемое от comparefn
, если x < y
, ноль, если x == y
, и положительное, если x > y
. x
и y
являются двумя членами Array , которые сортируются, и, поскольку эти элементы имеют одинаковую структуру, вы можете деструктурировать x
и y
как { day: xDay }, { day: yDay }
и вернуть xDay - yDay
для достаточного результата. В этом последнем фрагменте я представляю новую переменную cDay
, которая представляет собой String представление свойства day
. Я также реализую не перечисляемое свойство в конечных объектах s в Array и сортировщике на выходе.
const input = [{
"birth" : "23.04.1988",
"name": "Tom Smith"
},
{
"birth": "15.04.2010",
"name": "Mini Jall"
},
{
"birth": "23.04.2001",
"name":"Michael Bird"
}];
const dateCB = ({ birth, name }, idx, arr) => {
const dateString = birth.replace(/^(\d+)[^\d+](\d+)[^\d+](\d+)$/, (...backRefs) => {
return `${backRefs[3]}-${backRefs[2]}-${backRefs[1]}`;
});
const date = new Date(dateString);
const retObj = { day: date.getDate(), month: date.getUTCMonth() + 1, name };
return retObj;
};
const reducer = (prev, { day, month, name }) => {
const cDay = day.toString(10);
const retObj = { day: cDay, month, name: [name] };
Object.defineProperty(retObj, 'month', { enumerable: false });
if (prev.length === 0) {
prev.push(retObj);
} else {
const where = prev.findIndex(({ day: tDay, month: tMonth, name: tName }) => {
return tDay === cDay && tMonth === month;
});
if (where !== -1) {
prev[where].name
.push(name);
} else {
prev.push(retObj);
}
}
return prev;
};
const sorter = ({ day: bDay }, { day: aDay }) => {
return bDay - aDay;
};
const output = input.map(dateCB).reduce(reducer, []).sort(sorter);
console.log(JSON.stringify(output));