Выбрать главу

JSON

Функции высшего порядка, которые каким-то образом применяют функцию к элементам массива, широко распространены в JavaScript. Метод forEach – одна из самых примитивных подобных функций. В качестве методов массивов нам доступно много других вариантов функций. Для знакомства с ними давайте поиграем с ещё одним набором данных.

Несколько лет назад кто-то обследовал много архивов и сделал целую книгу по истории моей фамилии. Я открыл её, надеясь найти там рыцарей, пиратов и алхимиков… Но оказалось, что она заполнена в основном фламандскими фермерами. Для развлечения я извлёк информацию по моим непосредственным предкам и перевёл в формат, пригодный для чтения компьютером.

Файл выглядит примерно так:

[

  {"name": "Emma de Milliano", "sex": "f",

   "born": 1876, "died": 1956,

   "father": "Petrus de Milliano",

   "mother": "Sophia van Damme"},

  {"name": "Carolus Haverbeke", "sex": "m",

   "born": 1832, "died": 1905,

   "father": "Carel Haverbeke",

   "mother": "Maria van Brussel"},

  … и так далее

]

Этот формат называется JSON, что означает JavaScript Object Notation (разметка объектов JavaScript). Он широко используется в хранении данных и сетевых коммуникациях.

JSON похож на JavaScript по способу записи массивов и объектов – с некоторыми ограничениями. Все имена свойств должны быть заключены в двойные кавычки, а также допускаются только простые величины – никаких вызовов функций, переменных, ничего что включало бы вычисления. Также не допускаются комментарии.

JavaScript предоставляет функции JSON.stringify и JSON.parse, которые преобразовывают данные из этого формата и в этот формат. Первая принимает значение и возвращает строчку с JSON. Вторая принимает такую строчку и возвращает значение.

var string = JSON.stringify({name: "X", born: 1980});

console.log(string);

// → {"name":"X","born":1980}

console.log(JSON.parse(string).born);

// → 1980

Переменная ANCESTRY_FILE, доступная здесь, содержит JSON файл в виде строки. Давайте её раскодируем и посчитаем количество упомянутых людей.

var ancestry = JSON.parse(ANCESTRY_FILE);

console.log(ancestry.length);

// → 39

Фильтруем массив

Чтобы найти людей, которые были молоды в 1924 году, может пригодиться следующая функция. Она отфильтровывает элементы массива, которые не проходят проверку.

function filter(array, test) {

  var passed = [];

  for (var i = 0; i < array.length; i++) {

    if (test(array[i]))

      passed.push(array[i]);

  }

  return passed;

}

console.log(filter(ancestry, function(person) {

  return person.born > 1900 && person.born < 1925;

}));

// → [{name: "Philibert Haverbeke", …}, …]

Используется аргумент с именем test – это функция, которая производит вычисления проверки. Она вызывается для каждого элемента, а возвращаемое ею значение определяет, попадает ли этот элемент в возвращаемый массив.

В файле оказалось три человека, которые были молоды в 1924 – дедушка, бабушка и двоюродная бабушка.

Обратите внимание, функция filter не удаляет элементы из существующего массива, а строит новый, содержащий только прошедшие проверку элементы. Это чистая функция, потому что она не портит переданный ей массив.

Как и forEach, filter – это один из стандартных методов массива. В примере мы описали такую функцию, только чтобы показать, что она делает внутри. Отныне мы будем использовать её просто:

console.log(ancestry.filter(function(person) {

  return person.father == "Carel Haverbeke";

}));

// → [{name: "Carolus Haverbeke", …}]

Преобразования при помощи map

Допустим, есть у нас массив объектов, представляющих людей, который был получен фильтрацией массива предков. Но нам нужен массив имён, который было бы проще прочесть.

Метод map преобразовывает массив, применяя функцию ко всем его элементам и строя новый массив из возвращаемых значений. У нового массива будет та же длина, что у входного, но его содержимое будет преобразовано в новый формат.

function map(array, transform) {

  var mapped = [];

  for (var i = 0; i < array.length; i++)

    mapped.push(transform(array[i]));

  return mapped;

}

var overNinety = ancestry.filter(function(person) {

  return person.died - person.born > 90;

});

console.log(map(overNinety, function(person) {

  return person.name;

}));

// → ["Clara Aernoudts", "Emile Haverbeke",

//    "Maria Haverbeke"]