After Effects arrays and strings

TOC

Это небольшой справочник, который я решила сделать для себя и всех, кто часто использует выражения при работе в Adobe After Effects. Поскольку выражения являются подмножеством JS, то многие возможности ускользают от пользователей After Effects, мало знакомых с программированием. Я решила пройтись по JS и протестировать совместимые функции и методы. Надеюсь этот справочник поможет вам успешнее использовать выражения в вашей работе и применять больше генеративных возможностей. Этот справочник будет уточняться и расширяться. Все, что здесь написано, проверено для версии CC 2014.

1. Массивы

Массив (объект Array) это объект, в котором сохранено некоторое количество других объектов, где каждому из них сопоставлен последовательный номер (индекс элемента). Элементы называются объектами, т.к. не имеют четкого типа. Вы можете смешивать в массиве значения разных типов (например и числовые и текстовые). Но за этим кроются некоторые подводные камни. Для простоты будем пока рассматривать одномерный массив вида:

[1, 2, 3, 5, 7, "eight", -10]

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

Для нижеследующих примеров возьмем за исходный массив:

a = [1, 2, 3, 5, 7, 8, -10]

Я наверное опущу такие простые операции, как извлечение элемента массива вида b = a[3], когда в результате переменной b мы присваиваем значение третьего элемента. Не забывайте, что нумерация элементов массива начинается с нуля.

1.1 Сложение и вычитание массивов

b = a + 1

В данной операции правый операнд автоматически интерпретируется (type casting) как массив из одного элемента.
Таким образом единица добавится к нулевому элементу массива:

a + [1]              => [2, 2, 3, 5, 7, 8, -10]

Отсюда легко догадаться каков будет результат следующей операции.
К первым трем элемента будет добавлено по единице:

b = a + [1, 1, 1]    => [2, 3, 4, 5, 7, 8, -10]

1.2 Умножение и деление

Здесь все работает немного по-другому, и операция умножения или деления выполняется для всех элементов массива

b = a * 2           => [2, 4, 6, 10, 14, 16, -20]

Умножение массива на массив с помощью выражений АЕ невозможно.

1.3 Теперь рассмотрим функции объекта Math с массивами

Допустим нам надо вычислить значения синуса. Конструкция Math.sin(a) не сработает и вернет нам результат NaN. Поэтому самое время добавить к нашим знаниям метод применения функции ко всем элементам массива. Метод объекта массива map не вошел в АЕ, но зато вошел метод apply объекта Function. Итак применим функцию вычисления синуса к массиву:

b = Math.sin.apply([], a)

Конструкция выглядит немного странно, потому что формально мы применяем метод не к массиву, а к функции, и ей передаем массив в качестве аргумента. При этом наш массив это второй аргумент, первый аргумент указывает на прототип данных и нам не важен. Однако ж здесь нас ждет разочарование, результатом этой функции в АЕ будет значение только первого элемента. Пока я еще пытаюсь найти решение, однако же есть как минимум две очень полезные функции объекта Math, которые работают таким способом, это Math.max и Math.min. Таким способом мы можем быстро найти максимальное или минимальное значение в массиве:

b = Math.max.apply([], a)

или (по идее правильнее)

b = Math.max.apply(Math, a)   => 8

И тут давайте ввернем полезный метод slice объекта Array.
Он поможет нам отсечь часть массива, например:

b = a.slice(2,5)             => [3, 5, 7, 8]

Второй аргумент опциональный, если его нет, берется все до конца

b = a.slice(3)              => [5, 7, 8, -10]   

А теперь применим все вместе и найдем в массиве минимальное значение между вторым и пятым элементом:

b = Math.min.apply([], a.slice(2, 5))   => 3

1.4 Теперь все функции и методы массивов

Наш исходный массив все тот же:

a = [1, 2, 3, 5, 7, 8, -10]

Количество элементов в массиве (длина массива)

a.length  => 7

Преобразования массива в строку

a.toString()        => "1, 2, 3, 5, 7, 8, -10"
a.toLocaleString()

Преобразование в строку исходного кода

a.toSource()       => "[1, 2, 3, 5, 7, 8, -10]"

Объединение массивов/значений в массив последовательно

a.concat(32,53,75)    => [1, 2, 3, 5, 7, 8, -10, 32, 53, 75]
a.concat([32,53,75])  => [1, 2, 3, 5, 7, 8, -10, [32, 53, 75]]

Переворот массива (обратная последовательность элементов)

a.reverse()          => [-10, 8, 7, 5, 3, 2, 1]

Cлияние массива в строку с разделителем (символ разделителя указан в операнде)

a.join(";")          => "1;2;3;5;7;8;-10"

Cортировка массива (по возрастанию)

a.sort()             => [-10, 1, 2, 3, 5, 7, 8]

Аргументом функции sort может выступать другая функция, но в АЕ этот метод похоже не работает

Извлечение части массива

// по первому и последнему индексам элементов
a.slice(4)       => [7, 8, -10]
a.slice(3, 5)    => [5, 7] 

// по индексу и количеству элементов
a.splice(2)      => [3, 5, 7, 8, -10]
a.splice(2,4)    => [3, 5, 7, 8]

В JS этот метод может так же удалять и вставлять значения в массив, но не поддерживается в АЕ

Все предыдущие методы работы с массивами возвращали измененные значения, следующие же методы изменяют сам массив, поэтому в примерах указываются два значения, первое - новое значение массива a, второе - возвращаемое значение b.

Отброс последнего элемента массива с укорочением его длины, возвращает выброшенное значение (последний элемент)

b = a.pop()     => a = [1, 2, 3, 5, 7, 8] b = -10

Такая же операция но с нулевым элементом массива (сдвиг массива)

b = a.shift()  => a = [2, 3, 5, 7, 8, -10] b = 1

Вставка значений

// в конец массива с увеличением его длины, возвращает новую длину массива  
b = a.push(-9,-8,-7)         => b = [1, 2, 3, 5, 7, 8, -10, -9, -8, -7]    a = 10
b = a.push([-9,-8,-7])       => b = [1, 2, 3, 5, 7, 8, -10, [-9, -8, -7]]  a = 8 вставленный элемент тоже массив

// в начало массива с увеличением его длины, возвращает новую длину массива  
b = a.unshift(-2, -1, 0)     => b = [-2, -1, 0, 1, 2, 3, 5, 7, 8, -10, -9, -8, -7]   a = 10
b = a.unshift([-2, -1, 0])   => b = [[-2, -1, 0], 1, 2, 3, 5, 7, 8, -10, -9, -8, -7] a = 8 вставленный элемент тоже массив

2. Строки

Не будем рассматривать бестолковые методы html-форматирования, они нам вряд ли понадобятся. Вот их перечень на случай, если вы захотите оформить что-то в html: .anchor .big .blink .bold .fixed .fontcolor .fontsize .italics .link .small .strike .sub .sup

Создание строки s

s = "something"               => "something"
s = String("something")       => "something"
s = new String("something")   => "something"
s = String(a)                 => "1, 2, 3, 5, 7, 8, -10"

Строки (объект String) по сути тоже являются массивами, но все их элементы это символы. Поэтому некоторые методы строк очень схожи с методами массивов. Так же как и с массивами вы можете обращаться к символам внутри строки по индексу, нумерация так же начинается с нуля.

Для всех следующих примеров возьмем за основу строку:

str = "test this thing"

Строка является по сути символьным массивом:

str[6]         => "h" 
str.charAt(6)  => "h"

Длина строки (количество символов)

str.length    => 15

Индекс первого найденного включения строки (символа или символов) в строке

str.indexOf("i")         => 7
str.indexOf("thing")     => 10
str.indexOf("x")         => -1

Индекс последнего найденного включения строки (символа или символов) в строке

str.lastIndexOf("i")     => 12
str.lastIndexOf("thing") => 10
str.lastIndexOf("a")     => -1

Извлечение строк

str.substr(m,[length])  // начиная с индекса по количеству символов  
str.slice(a,b)          // между указанными индексами
str.substring(m,[n])    // межу казанными индексами, n - опционально
str.split("s", [n])     // разбиение строки на массив строк, разделитель - символ s, n - лимит. Пример `str.split(" ", 3) => ["test", "this", "thing"]
str.charCodeAt()        // код символа по индексу (позиции) в строке

Сложение строк

str + str2
str.concat(str2)

a = ["hello","there","!"]
a.join(" ")            => "Hello there !"

Сравнение, поиск

str.localeCompare()        // сравнение строк с кучей опций (см. документацию по Java)  
str.match(regexp)          // проверка строки на соответствие Regular Expression  (см. документацию по Regular Expressions)
str.replace(regexp,str2)   // замена подстроки по Regular Expression. Пример: `ss.replace(/thing/gi,"thing")` => "test this thing"
str.search("s" or regexp)  // поиск внутри строки s или Regular Expression

Регистр

str.toLocaleLowerCase()
str.toLowerCase()          // перевод строки в нижний регистр  
str.toLocaleUpperCase()
str.toUpperCase()          // перевод строки в верхний регистр  

Конвертация

str.toString()
str.valueOf()
parseInt(string)  // извлечение целого числа из строки

2.1 Дополнительно полезно:

instanceOf                        // тест на тип данных объекта. Пример: `a instanceOf Array = true`
eval(string)                      // вычисление арифметического выражения внутри строки  
("00000000" + Number).substr(-n)  // Добавить n нулей перед числом Number  
function.length                   // количество аргументов функции  
function.arguments()              // аргументы функции  
function.name()                   // имя функции  
function.prototype.call()         // вызов функции как метод (похоже на apply но к набору значений)  
function.toString()               // получение исходного кода функции (если функция объяdлена в коде)  
str.toSource()                    // исходный код объявления строки

3. Примеры

Перевернуть порядок слов (разделитель - пробел)

s.split(' ').reverse().join(' ')  => "thing this test"

Сумма элементов массива. Здесь массив записывается в виде строки суммы и затем вычислется с помощью инструкции eval

eval(valList.join('+'))

Минимальный элемент массива

b = Math.min.apply(Math, a)

Максимальный элемент массива

b = Math.max.apply(Math, a)