Комментарии 58
Скурпулезных замеров не проводил, но вот есть небольшой бенчмарк https://github.com/xonixx/jsqry/blob/master/bench.js.
У меня 100000 прогонов запроса вида
jsqry.one(o1, '[_.id>=2].name[_.toLowerCase()[0]==?].length', 's')
отрабатывает за 425ms.
За что я люблю LiveScript, так это за то, что подобные вещи можно достаточно компактно писать без библиотек.
До:
query([1,2,3,4,5],'[_>2]{_+10}')
После:
[1,2,3,4,5].filter(-> it>2).map(-> it+10)
it
это стандартная для LiveScript переменная, обозначающая первый аргумент функции. И никакого парсинга в рантайме не нужно.
Или же ES2016, если странспайлер используется:
[1,2,3,4,5].filter(it => it>2).map(it => it+10)
Спасибо, интересно.
А такой юз-кейс:
var hotel = {
name: 'Name',
facilities: [
{name:'Fac 1',
services: [
{name:'Service 1', visible:false},
{name:'Service 2'}
]},
{name:'Fac 1',
services: [
{name:'Service 3'},
{name:'Service 4', visible:false},
{name:'Service 5'}
]}
]
};
console.info(query(hotel,'facilities.services[_.visible !== false].name')); // [ 'Service 2', 'Service 3', 'Service 5' ]
Array.prototype.concatItems = function(){ return Array.prototype.concat.apply([], this) };
hotel.facilities.map(_=>_.services).concatItems().filter(_=>_.visible !== false).map(_=>_.name); // [ 'Service 2', 'Service 3', 'Service 5' ]
и не такой красивый
[].concat.apply([], hotel.facilities.map(_=>_.services)).filter(_=>_.visible !== false).map(_=>_.name); // [ 'Service 2', 'Service 3', 'Service 5' ]
hotel.facilities.map(_=>_.services).reduce((a,b)=>a.concat(b)).map(_=>_.name);
Замечу, что у такого решения есть недостаток — вычислительная сложность у него O(n*m) где n — суммарное количество элементов в подмассивах, m — количество подмассивов. У concat это O(n).
Если нужна лаконичнось, все укладывается в 1 reduce:
hotel.facilities.reduce(
(acc, f) => acc.concat(f.services.filter(s => s.visible !== false).map(s => s.name)),
[]
);
// ["Service 2", "Service 3", "Service 5"]
Если нужна скорость — пожалуйста.
hotel.facilities.reduce((acc, f) => {
f.services.forEach(s => {
if (s.visible !== false) {
acc.push(s.name);
}
});
return acc;
}, []);
// ["Service 2", "Service 3", "Service 5"]
Это я все к тому, что вариантов много и есть возможность использовать нужный, потому-что JS гибок. Но данная библиотека только повышает количество возможных способов, хотя краевые задачи все-равно придется решать с использованием нативных средств.
Но данная библиотека только повышает количество возможных способов
Не только. Её вариант проще Вашего лаконичного варианта, правильно обрабатывает null/undefined, работает на любом древнем JS.
По-моему плюсов не так и мало.
query(hotel,'facilities.services[_.visible !== false].name')
там значение undefined вообще можно переопределить
Тот кто это сделает — будет гнусным извращенцем. За то, как поведёт себя код в руках гнусного извращенца, разработчик не отвечает.
Что значит правильно обрабатывает
Как Ваши варианты решения отработают на какой-то из записей facilities без поля services?
Это гибко и удобно, хоть и не позволяет сэкономить лишние пару строк.
Forgiving: In JavaScript, trying to evaluate undefined properties generates ReferenceError or TypeError. In Angular, expression evaluation is forgiving to undefined and null
Весьма верное замечание! Вариант с Jsqry корректно прожует вариант объекта без поля services. Вариант с ES-кунг-фу скорее всего упадет с Null Pointer.
«ES-кунг-фу» гораздо более предсказуемо, его проще и удобнее отлаживать и, что самое важное, поддерживать — это часть стандарта.
Я бы реализовал следующим образом.
LiveScript:
[].concat(...hotel.facilities.map(-> it.services.filter(-> it.visible!==false).map(-> it.name)))
ES2016:
[].concat(...hotel.facilities.map(it => it.services.filter(it => it.visible!==false).map(it => it.name)))
Да, этот конкретно вариант уже более многословен, тем не менее никаких библиотек не нужно и сторонний наблюдатель с относительной легкостью сможет понять что происходит. Хотя в одну строчку в реальном проекте я бы не писал:)
var name;
for (var i = 0; i < users.length; i++) {
if (users[i].id == 123) {
name = users[i].name;
break;
}
}
В этом смысле так же как и регулярные выражения, JQuery-селекторы или тот же SQL.
А что — разве будет легче дебажить?
Ну когда запрос настолько сложен, что требует дебага, можно и в коде переписать.
А так-то можно и в Jsqry, хотя, признаться, о таком способе использования я раньше не думал.
function f1(elt) { return elt > 2 }
function f2(elt) { return elt + 10 }
console.info(jsqry.query([1,2,3,4,5],'[ ?(_) ]{ ?(_) }', f1, f2)); // [ 13, 14, 15 ]
Из приятного (что лично нравится мне) — это CSS selector-like way для выборки данных (то есть довольно интуитивно для веб-девелоперов), есть тесты, и есть подтверждение востребованности в виде большого количества девелоперов обративших на это внимание.
Тогда вот мой примитивный поиск по xpath в объекте
Библиотеки очень похожи по задумке. Jsqry значительно проще как по устройству так и по использованию за счет переиспользования обычного js для предикатов, в JSPath же изобрели свой язык предикатов со своими операторами и т.д. По функционалу, имхо 80-90% совпадает. Хотя в Jsqry срезы более продвинутые (поддерживется step параметр, как в Python), а также не нашел трансформации у них, только фильтрация. С другой стороны, в JSPath, разумеется есть и возможности, коих нет в Jsqry, например, '..' и '|' в location path. А, да, у них еще более громоздкий синтаксис подстановок — именованные вместо '?'. Впрочем, это можно расценить и как плюс.
Если серьезно, то по-моему после появления arrow-functions в ES6 или typescript (в котором они были от рождения), это всё на столько бесполезно…
Нечто похожее я сделал для ActionScript, но сделал это на функциях и как часть библиотеки для работы с коллекциями. В принципе, я согласен с тем, что arrow-functions эту нишу во многом закрывают.
Лично я планирую использовать эту библиотеку.
А то, что эту задачу можно решить в ES6 или typescript, ну так отлично. Ее можно решить используя еще множество других инструментов.
В данном случае мне нравится, что это JS изначально, а не «обертки», да и размер самой библиотеки для меня является тоже плюсом.
Jsqry — библиотека для запросов к JS объектам и массивам