Javascript-форум (https://javascript.ru/forum/)
-   Ваши сайты и скрипты (https://javascript.ru/forum/project/)
-   -   Collection v5 (https://javascript.ru/forum/project/47717-collection-v5.html)

kobezzza 04.06.2014 19:48

Collection v5
 
Всем здрасти!:)

Закончил работу над Collection 5.

Для тех, кто не в курсе, то что такое Collection.

И так, что нового:

1) Поддержка всех новых видов коллекций и технологий, а именно: Map, Set, WeakMap, WeakSet, генераторов, итераторов на основе протокола @@iterator.

var foo = new Map([[{}, 1], [null, 2]]);

$C(foo).map(el => el * 2) // new Map([[{}, 2], [null, 4]])

function *i() {
    for (let i = 0; i < 10; i++) {
        yield i;
    }
}

$C(i).get(el => el > 5) // [6, 7, 8, 9]


Работает во всех последних версиях браузеров (включая ИЕ).

2) Новый пласт методов и свойств для работы в контексте конкретного итератора.

$C([1, 2, 3, 4]).forEach(function (el) {
    if (el == 2) {
        this.break();
    }
});

$C([1, 2, 3, 4]).get(function (el) {
    if (el == 2) {
        this.modi(1);
    }

    return true;
}); // [1, 2, 4]


Подробнее в доке.

3) Новый метод filter (частный случай map), in, новые режимы для метода extend.

4) 100% code review который в итоге вылился в значительное улучшение интерфейсов методов (собственно поэтому версия 5, а не 4.3).

Основные изменения по сравнению с 4-й версией:

4.1) Метод remove возвращает массив или объект удалённых элементов;
4.2) Метод set возвращает массив или объект обновлённых элементов;
4.3) Метод add возвращает объект статуса добавления;
4.4) Параметр методов useForIn был заменён на более универсальный use с опциями работы: 'for', 'for in', 'for of'.
4.5) Метод groupKeys был удалён в пользу простого group с параметров saveKeys.

В остальном улучшения интерфейсов не нарушили обратную совместимость.

5) Более 900 новых тестов, а также заменён qUnit на Jasmine 2.
6) Улучшен модуль генерации кода, что дало в некоторых случаях дополнительный прирост производительности.
7) Оптимизация методов search и remove с помощью нового АПИ контекста итераторов позволило увеличить скорость работы в 2 раза.
8) Расширен механизм указателей для работы с новыми видами коллекций.
9) Исправлено множество ошибок, улучшены комментарии, теперь Collection на 100% проходит строгую проверку Closure Lint.
10) Проведена дополнительная оптимизация кода для работы в WebStorm (в результате которого было отправлено более 20-ти багов для JetBrains :) ) - теперь работает почти идеальный автокомплит и в WebStorm последней версии включены множество исправлений :)
11) Значительно улучшена документация и перенесена в Wiki.

Собственно, что пишу сейчас: кому не лень, почитайте имеющиеся доку на Wiki и скажите замечания и т.д. т.к. доку для меня писать самое сложное :)

cyber 04.06.2014 19:52

Круто)
Цитата:

Сообщение от kobezzza
Собственно, что пишу сейчас: кому не лень, почитайте имеющиеся доку на Wiki и скажите замечания и т.д. т.к. доку для меня писать самое сложное

на выходных по читаю, отпишусь)

Octane 04.06.2014 20:05

а как ты реализуешь итератор для браузеров, которые поддерживают Map и Set, но не поддерживают @@iterator?

kobezzza 04.06.2014 20:07

Цитата:

Сообщение от Octane (Сообщение 314820)
а как ты реализуешь итератор для браузеров, которые поддерживают Map и Set, но не поддерживают @@iterator?

Написан полифил, который аккуратно добавляет поддержку без оверхедов, т.е. работает всё также быстро.

PS:

Наглядный пример использования протокола @@iterator в работе:

// Для более сахарного вида используем итератор на основе генератора
$C(function *() {
    var str = '1-2 4-5 6-7';
    var rgxp = /(\d+)-(\d+)/g;

    var tmp;
    while ((tmp = rgxp.exec(str))) {
        yield tmp;
    }
}).get() // [["1-2", "1", "2"], ["4-5", "4", "5"], ["6-7", "6", "7"]]

// Можно любому объекту или классу определить свойство @@iterator и Collection это поймёт
$C({'@@iterator': function *() {
    for (let i = 0; i < 10; i++) {
        yield i;
    }
}}).get() // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

cyber 04.06.2014 20:22

kobezzza, ты в полифили Map юзаешь let, на сколько я помню он поддерживается только лисой , как тогда работает в других браузерах ?

kobezzza 04.06.2014 20:25

Цитата:

Сообщение от cyber (Сообщение 314825)
kobezzza, ты в полифили Map юзаешь let, на сколько я помню он поддерживается только лисой , как тогда работает в других браузерах ?

Я использую транслятор ECMAScript6 в ECMAScript3. Тут об этом написано.

Сама либа работает в ИЕ6+, но кстати let и const работают уже во всех последних версиях браузеров.

PS:

Пример написания фильтра который отбирает только уникальные / не уникальные значения на основе нового АПИ контекста forEach.

$C().addFilter('unique', function (el, key, data, i, length) {
    if (!this.$.init) {
        let cache = this.$.cache = this.$.cache || new Set();
        let final = this.$.final = this.$.final || new Set();

        if (cache.has(el)) {
            final.delete(el);
        
        } else {
            final.add(el);
            cache.add(el);
        }

        if (i === (this._.endIndex || (length() - 1))) {
            this.jump(0);
            this.$.init = true;
        }

        return this.FALSE;
    }
    
    return this.$.final.has(el);
});

$C([1, 2, 3, 2, 3, 9]).get('unique') // [1, 9]
$C([1, 2, 3, 2, 3, 9]).get('!unique') // [2, 3, 2, 3]

kobezzza 09.06.2014 16:14

Юху! Я сделал это :) Collection 5 доведён до релиза :dance:

PS:
Как же приятно доводить дело до конца :)
Сейчас смотрю:

*) 9.5+к строк кода
*) 1000+ тестов
*) Вики по 100% функционалу

:)

PSPS:
Таки решил выделить день на написание статьи на Хабр, надеюсь завтра к вечеру закончу.

monolithed 09.06.2014 21:57

Цитата:

Сообщение от kobezzza
*) 9.5+к строк кода

Извини за нескромный вопрос, но зачем это все нужно? :)

kobezzza 09.06.2014 23:10

Цитата:

Сообщение от monolithed (Сообщение 315783)
Извини за нескромный вопрос, но зачем это все нужно? :)

тут же написано

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

monolithed 09.06.2014 23:37

Цитата:

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

А как же принцип "Не плати за то что не используешь"?

kobezzza 09.06.2014 23:43

Цитата:

Сообщение от monolithed (Сообщение 315800)
А как же принцип "Не плати за то что не используешь"?

Это ща к чему? Есть модульная сборка, можно сделать свой билд. Скорость работы либы такая же, как если бы всё хардкодить на циклах, т.е. опять таки никаких переплат, а вот профит очевиден.

Да и максимальная сборка либы - это всего 16 килобайт, объём такой же как и у underscore, а функционал несоизмеримо больше, а про скорость я вообще молчу.

В общем на JavaScript.ru как обычно: "описание не читал, но по любому не нужон" :)

monolithed 10.06.2014 00:17

Цитата:

Сообщение от kobezzza
В общем на JavaScript.ru как обычно: "описание не читал, но по любому не нужон"

Да нет, слежу с первой версии... просто пока не могу понять где бы эта либа пригодилась в моей работе :)

kobezzza 10.06.2014 00:24

Цитата:

Сообщение от monolithed (Сообщение 315810)
Да нет, слежу с первой версии... просто пока не могу понять где бы эта либа была полезна :)

Ты не мог следить с первой версии, т.к. первая публикация была с 3-й :)

Эта либа общего назначения, т.е. использовать её можно везде, все мои проекты (кроме Snakeskin, т.к. только в 5-й версии был добавлен механизм "контекста forEach") используют Collection. Одно "но", чтобы писать на Collection нужно любить функциональщину, т.е. не шарахаться от map / reduce и прочего, т.к. либа явно декларирует именно такой подход.

Пример, функция которая преобразовывает dash-style в camelCase

String.prototype.convertDashStyleToCamelCase = function () {
    return $C(this).reduce(function (res, el) {
        if (el !== '-') {
            res += this.$.up ? el.toUpperCase() : el;
        }

        this.$.up = el === '-';
        return res;
    
    }, '');
};


Обратная операция

String.prototype.convertCamelCaseToDashStyle = function () {
    return $C(this).reduce(function (res, el) {
        var lc = el.toLowerCase();
        return res + (el !== lc ? '-' : '') + lc;
    }, '');
};


Отбор элементов для пагинации

$C(...).forEach(function (el) {
        ...
    
}, {startIndex: 10, count: 10});


Тоже самое, но в обратном порядке

$C(...).forEach(function (el) {
        ...
    
}, {startIndex: 10, count: 10, reverse: true});


Количество вхождений каждого символа в строке

$C('fooobar1212').group(el => el)


Количество вхождений буквы 'o' в строке

$C('fooobar1212').length(el => el == 'o')


Преобразовать объект {ключ: значение} в Set из значений, которые больше 1


$C({a: 1, b: 2, c: 3}).map(el => el, {initial: new Set, filter: el => el > 1})


При этом не важно, что мы итерируем, хоть массив, объект, Map, Set, генератор, строку и т.д. все методы работают одинаково и со скоростью циклов, но в сахарном функциональном виде.

Примеров можно сделать уйму, но достаточно взглянуть на методы: https://github.com/kobezzza/Collection/wiki

kobezzza 10.06.2014 14:04

Написал короткую описательную статейку на хабр

monolithed 10.06.2014 15:08

Цитата:

Сообщение от kobezzza
Ты не мог следить с первой версии, т.к. первая публикация была с 3-й

Ага, спиздел. Короче с первой статьи на хабре :)

melky 10.06.2014 19:54

Цитата:

Сообщение от kobezzza
чтобы писать на Collection нужно любить функциональщину, т.е. не шарахаться от map / reduce и прочего, т.к. либа явно декларирует именно такой подход.

вот у меня появилась мысль форкнуть LiveScript и примешать туда $C

т.е. пишем на обычном LS завёрнутую конструкцию, а это дело разворачивается в один вызов $C

без всяких "var $C = require("Collection")"

kobezzza 10.06.2014 19:59

Цитата:

Сообщение от melky (Сообщение 315987)
вот у меня появилась мысль форкнуть LiveScript и примешать туда $C

Гм, мне кажется это слишком сурово, т.е. ты хочешь на уровне синтаксиса интеграцию сделать?)

Цитата:

Сообщение от melky (Сообщение 315987)
без всяких "var $C = require("Collection")"

Я делаю так (в ноде): в файле конфига пишу

var collection = require("collection.js");

global.$C = collection.$C;
global.Collection = collection.Collection;


И всё, можно нигде больше не кидать require :)

PS: следует заметить, что я не против require, просто в случае с Collection его нужно подключать везде и это утомляет :)

melky 10.06.2014 20:04

Цитата:

Сообщение от kobezzza
Гм, мне кажется это слишком сурово, т.е. ты хочешь на уровне синтаксиса интеграцию сделать?)

да.

хотя...

спад производительности почти незаметен в LS... а если нужны производительные части, можно вызвать $C )

Цитата:

Сообщение от kobezzza
Я делаю так (в ноде): в файле конфига пишу

точно! забыл про глобали

kobezzza 10.06.2014 20:11

Цитата:

спад производительности почти незаметен в LS... а если нужны производительные части, можно вызвать $C
Странный подход, не спорю, что Collection быстрый, но это не главное, как мне кажется :)

Я то хотел сделать единое навороченное функциональное АПИ для всех видов итерируемых данных :)

kobezzza 10.06.2014 22:22

Выпустил микроапдейт: 5.0.1.

melky 10.06.2014 22:40

Цитата:

Сообщение от kobezzza (Сообщение 316006)
Выпустил микроапдейт: 5.0.1.

changelog кстати очень легко делается из вывода 'git log' с форматированием :)

kobezzza 10.06.2014 22:55

Цитата:

Сообщение от melky (Сообщение 316008)
changelog кстати очень легко делается из вывода 'git log' с форматированием :)

Кстати я учёл твою критику по поводу плохих коммитов, теперь стараюсь :) Вообще надо бы и history и changelog сделать, но вечно то ленюсь, то времени нет. Теперь по графику давно задуманный апдейт Snakeskin, а потом опять прыгаю на свой основной проект :)

monolithed 10.06.2014 23:01

Цитата:

Сообщение от kobezzza
И всё, можно нигде больше не кидать require

Хм. а мне наоборот нравится писать заголовочные файлы и использовать неймспейсы.

Вообще мечтаю когда будет:

import std;

std::math.round(...);
std::number.parse_int(...);

А не эта шляпа:

Math.round
parseInt

Octane 10.06.2014 23:20

parseInt ➝ Number.parseInt

kobezzza 10.06.2014 23:22

Цитата:

Сообщение от Octane (Сообщение 316012)
parseInt ➝ Number.parseInt

Кстати да, я тоже не понимаю, почему эта функция глобальная. Или тоже взять например btoa и atob - их же логично запихнуть в String. Они каждую спецификацию годами мучают, вроде должны всё обдумать, а всё равно странные вещи есть.

monolithed 11.06.2014 09:05

Цитата:

Сообщение от kobezzza
Кстати да, я тоже не понимаю, почему эта функция глобальная.

В ES6 теперь так

Цитата:

Сообщение от Octane
parseInt ➝ Number.parseInt

...
Цитата:

Сообщение от kobezzza
Или тоже взять например btoa и atob - их же логично запихнуть в String.

По моей логике должны быть в utils или algorithms :)

Octane 11.06.2014 10:59

жесть в том, что
Number.parseInt === parseInt ➝ true

но
Number.isNaN === isNaN ➝ false

и поведение у isNaN разное

Aetae 11.06.2014 11:49

Octane, ну какбэ логично: старый isNaN будет deprecated, но останется для совместимости. Новые скрипты надо будет писать сразу по новому.
На счёт оправданности нового поведения ничего сказать не могу.)

kobezzza 11.06.2014 16:11

Написал ещё одну коротенькую статью: http://habrahabr.ru/post/225995/

monolithed 11.06.2014 21:50

В ноде мне часто нужны extend, extend_deep, clone, clone_deep, все остальное как мне кажется лишнее...., т.к. for/of поддерживает даже итератор протокол.

Может сделать микро-либу только для ноды? :)

kobezzza 11.06.2014 21:51

Цитата:

for/of поддерживает даже итератор протокол.
Я бы мог поспорить, но не буду, т.к. устал доказывать, юзайте циклы :)

Цитата:

Может сделать микро-либу только для ноды?
Зачем думать в ноде о размере файла ? Оо

monolithed 11.06.2014 21:59

Цитата:

Сообщение от kobezzza
Я бы мог поспорить, но не буду, т.к. устал доказывать, юзайте циклы

Да я не и не хотел спорить, что там с for/of не так? :)
Цитата:

Сообщение от kobezzza
Зачем думать в ноде о размере файла ? Оо

В lodash кроме extend я ничего не использую, поэтому и не понимаю зачем там все остальное...

kobezzza 11.06.2014 22:05

Цитата:

Да я не и не хотел спорить, что там с for/of не так?
вот и юзай его :)

monolithed 11.06.2014 22:08

Цитата:

Сообщение от kobezzza
вот и юзай его

Я не понимаю твоего сарказма, что в нем не так?
break, continue, @@iterator, yield, есть, условные выражения, что еще нужно?

Вижу только один минус — hasOwnProperty вроде не проверяет и @@iterator для строк хотят убрать

А вообще мне нода очень сильно не нравится..... полдня искал нормальный вотчер для файлов, около двадцати проектов выкинул в поймоку, т.к. они либо не работают, либо работают криво, в итоге остановился на chokidar

kobezzza 11.06.2014 22:15

Цитата:

Я не понимаю твоего сарказма, что в нем не так?
break, continue, @@iterator, yield, есть, условные выражения, что еще нужно?
Кроме того, что мне придётся снова и снова описывать рутину, раздувать код неведомой хренью... рано или поздно ты начнёшь создавать хелперы и через несколько лет придумаешь свой Collection :D

// Вернуть массив элементов исходной коллекции, которые
$C(new Map(...)).get({
    reverse: true, // В обратном порядке
    startIndex: 10, // Начиная с 10-го итерационного индекса
    from: 5, // Пропустить 5 успешных итераций
    count: 10 // Не более 10 успешных итераций (не считая from),
    filter: 'unique && !odd' // Только уникальные не чётные элементы
})


Напиши это на for of и ужаснись. А вообще давай не будем спорить, можешь не отвечать.

monolithed 11.06.2014 22:32

Цитата:

Сообщение от kobezzza
А вообще давай не будем спорить

Спор тут не уместен.
Я просто хочу услышать от тебя, насколько часто ты сам используешь такие алгоритмические "цепочки" и на каких задачах ты понял что нужен Collection?

kobezzza 11.06.2014 22:36

Цитата:

Я просто хочу услышать от тебя, насколько часто ты сам используешь такие алгоритмические "цепочки"?
Я юзаю постоянно и везде, я уже неск лет юзаю циклы крайне редко, т.к. функциональное программирование (ИМХО) гораздо лучше.

Данный пример выдран из контекста, где идёт реализация пагинации.

Цитата:

и на каких задачах ты понял что нужен Collection?
Было это очень давно, императивщина угнетала меня, началось с того, что меня взбесило постоянно писать hasOwnProperty, и я написал хелпер forIn, потом другой, потом третий первые 2 версии Collection были похожи на lodash - клоака хелперов из 100500 методов. Потом я стал это дело причёсывать: сократил количество методов выделив базисные и снабдив их параметрами, добавил полиморфизма, потом ещё мне нужна была синхронизация с локальным хранилищем - вкрутил и её. Collection родился под влиянием проектов над которыми я работал и работаю.

monolithed 11.06.2014 22:40

Цитата:

Сообщение от kobezzza
я уже неск лет юзаю циклы крайне редко

Я уже несколько лет крайне редко вообще что-то итерирую :D
Цитата:

Сообщение от kobezzza
т.к. функциональное программирование (ИМХО) гораздо лучше.

Истинная любовь к ФП приходит после знакомства с Erlang, Haskell или F#:)

kobezzza 12.06.2014 22:03

Выпустил апдейт v5.0.2.

Главное нововведение: это новая сборка collection.light, которая включает в себя только набор основных итераторов и весит ~9кб (т.е. почти в 2 раза меньше полной сборки).

kobezzza 15.06.2014 23:31

Выпустил апдейт 5.0.3. Теперь для обхода объекта, если не задан флаг notOwn и доступен нативный Object.keys применяется стратегия: keys + for, что дало заметный прирост производительности.


Часовой пояс GMT +3, время: 11:19.