
Здесь представлено 13 однострочников. Примеры подготовлены с использованием Node.js v11.x. Если вы будете использовать их в другой среде — это может повлиять на их выполнение.
1. Приведение значений к логическому типу
Вот как привести некое значение к логическому типу:
const myBoolean = !!myVariable;
Двойное отрицание (
!!
) нужно для того, чтобы значение, являющееся с точки зрения правил JavaScript истинным, было бы преобразовано в true
, а ложным — в false
.2. Избавление от повторяющихся значений в массивах
Вот как удалить из массива повторяющиеся значения:
const deDupe = [...new Set(myArray)];
Структуры данных типа
Set
хранят лишь уникальные значения. В результате использование такой структуры данных и синтаксиса spread позволяет создать на основе массива myArray
новый массив, в котором нет повторяющихся значений.3. Создание и установка свойств объектов по условию
Для того чтобы задавать свойства объектов с использованием оператора
&&
, можно воспользоваться синтаксисом spread:const myObject = { ...myProperty && { propName: myProperty } };
Если в результате вычисления левой части выражения будет получено нечто, воспринимаемое JS как ложное значение, то
&&
не будет проводить дальнейшие вычисления и новое свойство не будет создано и установлено. Объект myObject
будет пустым. Если же конструкция ...myProperty
вернёт какой-то результат, воспринимаемый JS как истинный, благодаря конструкции &&
в объекте появится свойство propName
, хранящее полученное значение.4. Слияние объектов
Вот как можно создать новый объект, в котором будут скомбинированы два других объекта:
const mergedObject = { ...objectOne, ...objectTwo };
Этот подход можно использовать для организации слияния неограниченного числа объектов. При этом если у объектов будут свойства с одинаковыми именами, в итоговом объекте останется лишь одно такое свойство, принадлежащее тому из исходных объектов, который расположен правее других. Обратите внимание на то, что здесь используется мелкое копирование свойств объектов.
5. Обмен значений переменных
Для того чтобы обменять значения между двумя переменными без использования вспомогательной переменной можно поступить так:
[varA, varB] = [varB, varA];
После этого то, что было в
varA
, попадёт в varB
, и наоборот. Это возможно благодаря использованию внутренних механизмов деструктурирования.6. Удаление ложных значений из массива
Вот как можно убрать из массива все значения, которые считаются в JavaScript ложными:
const clean = dirty.filter(Boolean);
В ходе выполнения этой операции из массива будут удалены такие значения, как
null
, undefined
, false
, 0
, а так же пустые строки.7. Преобразование чисел в строки
Для того чтобы преобразовать числа, хранящиеся в массиве, в их строковое представление, можно поступить так:
const stringArray = numberArray.map(String);
Строковые элементы массива в ходе подобного преобразования так и останутся строковыми.
Можно выполнить и обратное преобразование — преобразовав значения типа
String
к значениям типа Number
:const numberArray = stringArray.map(Number);
8. Извлечение значений свойств объектов
Вот как можно извлечь значение свойства объекта и записать его в константу, имя которой отличается от имени этого свойства:
const { original: newName } = myObject;
Благодаря использованию этой конструкции будет создана новая константа,
newName
, в которую будет записано значение свойства original
объекта myObject
.9. Форматирование JSON-кода
Вот как можно преобразовать JSON-код к виду, удобному для восприятия:
const formatted = JSON.stringify(myObj, null, 2);
Метод
stringify
принимает три параметра. Первый — это JavaScript-объект. Второй, необязательный, представляет собой функцию, которую можно использовать для обработки JSON-кода, получающегося в ходе преобразования объекта. Последний параметр указывает на то, сколько пробелов нужно использовать при формировании отступов в JSON-коде. Если опустить последний параметр, то весь полученный JSON-код будет представлять собой одну длинную строку. Если в объекте myObj
есть циклические ссылки, преобразовать его в формат JSON не удастся.10. Быстрое создание числовых массивов
Вот как можно создать массив и заполнить его числами:
const numArray = Array.from(new Array(52), (x, i) => i);
Первый элемент такого массива имеет индекс 0. Размер массива можно задавать как с помощью числового литерала, так и с помощью переменной. Здесь мы создаём массив из 52 элементов, который, например, можно использовать для хранения данных о колоде карт.
11. Создание кодов для двухфакторной аутентификации
Для того чтобы сгенерировать шестизначный код, используемый в механизмах двухфакторной аутентификации или в других подобных, можно сделать так:
const code = Math.floor(Math.random() * 1000000).toString().padStart(6, "0");
Обратите внимание на то, что количество нулей в числе, на которое умножается результат, возвращаемый
Math.random()
, должно соответствовать первому параметру (targetLength
) метода padStart
.12. Перемешивание массива
Для того чтобы перемешать массив, не зная при этом о том, что именно в нём содержится, можно сделать так:
myArray.sort(() => { return Math.random() - 0.5});
Существуют и более качественные алгоритмы для перемешивания массивов. Например — алгоритм тасования Фишера-Йетса. Почитать о разных алгоритмах для перемешивания массивов можно здесь.
13. Создание глубоких копий объектов
Предлагаемый здесь метод глубокого копирования объектов не отличается особенно высокой производительностью. Но если вам нужно решить эту задачу с использованием однострочника — можете воспользоваться следующим кодом:
const myClone = JSON.parse(JSON.stringify(originalObject));
Надо отметить, что если в
originalObject
есть циклические ссылки, то создать его копию не удастся. Эту технику рекомендуется использовать на простых объектах, которые вы создаёте сами.Мелкую копию объекта можно создать с помощью синтаксиса spread:
const myClone = { ...orignalObject };
Итоги: о комбинировании и расширении кода однострочников
Существует огромное количество способов комбинации представленных здесь фрагментов кода. Это позволяет решать с их помощью множество задач и при этом пользоваться весьма компактными языковыми конструкциями. Автор этого материала полагает, что в ходе эволюции JavaScript в нём ещё появятся новые возможности, которые будут способствовать написанию компактного и мощного кода.
Уважаемые читатели! Какими примерами полезных JS-однострочников вы дополнили бы этот материал?


Комментарии (60)
Aingis
20.06.2019 13:10+21–12: Мама, я прочитал документацию Javascript! Смотри, как я могу!
13: На самом деле такое копирование обладает рядом недостатков. JSON имеет в своём составе только примитивные структуры. В массивах
undefined
(и дырки) станутnull
. В объектах просто будут удалены. Типизированные массивы сконвертируются в обычные.Date
станет строкой. Все функции,Map
иSet
и некоторые другие вообще потеряются, подобноundefined
!
Разумеется, потеряются и
symbol
-ссылки, и любые другие неперечисляемые свойства (которых не будет вfor-in
/Object.keys()
), геттеры, сеттеры. Даже обычные объекты могут выдать что угодно, если у них (или в прототипе) есть метод.toJSON()
. На циклических ссылках код вообще упадёт.JustDont
20.06.2019 14:1713: На самом деле такое копирование обладает рядом недостатков.
Эти вот «недостатки» — они не обязательно недостатки, иногда они наоборот крайне полезны. Единственное, что тут нужно иметь в виду, что прогон объектов через JSON — это всё-таки НЕ клонирование.
И, кстати, оно очень даже быстро работает; делая клонирование через обход дерева объектов руками, вполне можно сделать даже медленнее.
99designs
21.06.2019 12:54Если понадобилось копировать сложный объект, скорее всего где то выше в архитектуре проблемы.
JustDont
21.06.2019 13:26Ну щас, ага.
У вас есть стейт приложения. Пользователь в этот стейт вносит изменения, которые должны будут примениться по нажатии кнопки «применить изменения». Как будете хранить «грязный» стейт? Как дельту к чистому? И если никакие undo-redo заведомо не нужны — всё равно будете дельты хранить? И будете писать код по созданию дельт и их накатыванию, вместо того, чтоб отклонировать чистый стейт, а потом залить грязный поверх чистого (в одну строчку)?Sirion
21.06.2019 13:42Immutable.js
JustDont
21.06.2019 13:49Угу. 64Кб жаваскрипта. Повторить двадцать раз на любой чих, а потом удивляться, чё это у нас ничего особенного не делающие страницы грузят мегабайты скриптов.
И к слову о этой ветке комментариев, Immutable.js — это как раз таки копирование объектов во все поля. Только зашитое в либу.Sirion
21.06.2019 15:34А вот и нет. Там хитрые внутренние механизмы, избегающие дублирования данных и лишней работы.
JustDont
21.06.2019 15:48Там хитрые внутренние механизмы, избегающие дублирования данных и лишней работы.
Никакая внутренняя хитрость не поможет вам сохранить два указателя на данные по той же цене, что и один указатель. Я в курсе, что immutable.js очень неплохо оптимизирован (и вообще либа концептуально хорошая), но то, что он делает — это всё равно копирование структур данных. Даже если копируется только левая сторона (указатели).
faiwer
21.06.2019 16:27Решал подобную задачу. Активно использовал иммутабельность (без immutable.js). В итоге для redo/undo не хранил никаких дельт, а хранил ссылку на предыдущий стейт целиком. Сами понимаете, вес одного undo-среза был крошечный.
Вопрос с сериализацией и десериализацией решил просто: кастомный cloneObject метод, который используя Map упаковывал state в json, где все circular references были строкой вида
$circular$firstPath
. И был такой же метод десериализации который строил такой же объект восстанавливая все ссылки. На всё про всё строк 30 кода.
Sirion
20.06.2019 14:010xd34df00d
20.06.2019 16:30Хорошо вам там. В плюсах аналогичный подход почти наверняка уронит sort и прочие алгоритмы, ожидающие корректный порядок.
Sirion
20.06.2019 22:00Вот прям уронит? Мне кажется, все быстрые алгоритмы сортировки должны такой компаратор нормально переварить, потому что у них тупо нет времени, чтобы наткнуться на неконсистентность. Потому что неконсистентность — это когда мы получили результат сравнения, противоречащий предыдущим результатам. Но если он противоречит им — значит, мы могли его знать ещё до того, как провели сравнение (предполагая, что компаратор «нормальный»).
0xd34df00d
20.06.2019 22:33+1Можно вылезти за границы массива, например, или уйти в бесконечную рекурсию/цикл, или ещё чего натворить. Вот я сконструировал пример, который валит libstdc++, примерно с третьей попытки.
Собственно, эта страница приоткрывает завесу: требуется, чтобы предикат описывал некоторый strict weak order со всей его аксиоматикой.
На CppNow 2019 был доклад Гашпера про порядки и как они относятся к C++20'овому operator spaceship, и в лицах ряда людей читалось непонимание математической части (хотя там ничего принципиально сложного не было, хоть я и дико не согласен с философией его подхода). Но это так, навеянные мысли вслух к вопросу о том, зачем программистам математика.
1andy
20.06.2019 17:48+2Вредный «однострочник»:
myArray.sort(() => { return Math.random() - 0.5});
Если хочется использовать Math.random(), то в одну строку примерно так
arr = arr.map(a => ({sort: Math.random(), value: a})).sort((a, b) => a.sort - b.sort).map(a => a.value)
Детали: stackoverflow.com/questions/962802/is-it-correct-to-use-javascript-array-sort-method-for-shuffling
Tannenfels
20.06.2019 19:23+1Давайте лучше полезные однострочники на Perl
red_andr
20.06.2019 19:57+1Или на Питоне. Мой любимый, квиксорт в одну строку:
qs = lambda L: [] if L==[] else qs([x for x in L[1:] if x<L[0]]) + L[0:1] + qs([x for x in L[1:] if x>=L[0]])
vp_arth
20.06.2019 20:09Не очень хороший выбор pivot. Должен плохо работать для почти отсортированных массивов.
Зато нагляднее некуда.red_andr
20.06.2019 20:13Да, тут скорее для наглядности или искусства ради. У него есть проблема есть и посерьёзнее, будучи рекурсивным не будет работать для списков больше определённого размера. Причём не очень большого, порядка тысячи.
the_o
21.06.2019 12:54+1Аж даже любопытно стало вспомнить перл и написать то же самое на нём =)
$qs = sub { my ($car, @cdr) = @_; @_ == () ? () : ($qs->(grep { $_ < $car} @cdr), $_[0], $qs->(grep { $_ >= $car} @cdr)) };
vp_arth
21.06.2019 13:45+1Ну и грех было бы не перевести на javascript, раз уж это тема статьи:
const qs = a=>a.length?[...qs(a.slice(1).filter(e=>e<a[0])),a[0],...qs(a.slice(1).filter(e=>e>=a[0]))]:[];
missingdays
20.06.2019 20:59+3что здесь используется мелкое копирование свойств объектов
translate.google.com/#view=home&op=translate&sl=en&tl=ru&text=shallow%20copying
Почему-то знал, что скажет google переводчик еще до того, как ввел в него эту фразу.
ArVaganov
21.06.2019 03:05
Удачи в копировании объектов с properties NaN, Infinity, Function, и многими другими.JSON.parse(JSON.stringify(originalObject));
ganqqwerty
21.06.2019 12:24Если честно, не люблю эти однострочники, некоторые из них просто щекочат эго джунов, не принося никакой пользы. Да, их надо знать, чтобы распознавать в чужом коде, но свой код должен быть кристально ясен.
Если вы удаляете дубликаты из массива, как насчет функции `removeDuplicates(arr, predicate)`? Вы можете реализовать ее оптимальным способом, она будет фурычить быстрее, а постороннему программисту будет сразу ясно — вот тут удаляют дубликаты. Ну или в каком-нибудь lodash такая функция наверняка есть. Вы можете гарантировать, что ваш код всегда будут читать любители и знатоки однострочников? Или может быть, в вашем проекте появляются иногда джуны, или переученные за две недели с джавы «фуллстэк-девелоперы»? Сколько времени это будет стоить вашей компании на промежутке в хотя бы год? Если ваша зарплата включает в себя процент от прибылей, такие вопросы — не праздные.
Мой вывод: такие вещи могут быть полезны только в случае если вы пишете код на выброс, для себя на один раз. Как только речь о серьезных продуктах и мало-мальских командах, такое ковбойство надо вымарывать.
ganqqwerty
21.06.2019 12:24Или вот пункт 8 — ну спотыкается взгляд о такие конструкции. А что если в объекте справа переименовалось свойство, а у вас нет тайпскрипта и приличной IDE, которая бы все подчеркнуло?
ganqqwerty
21.06.2019 12:31Пункт 13 — очень плохо давать такие советы. Во-первых, автор просто так ляпнул, что этот способ медленный. Он не медленный, он самый быстрый.
Во-вторых, применять этот способ нужно не когда у вас мало времени, а когда вы точно уверены, что объекты будут простенькие, без циклических ссылок или ссылок на какие-нибудь гигантские другие объекты и нужно чтобы это было супер-быстро, потому что делается миллиарды раз. Да я могу маленький движок графовой БД таким, что попытка `stringify` одного из узлов выведет полностью всю эту базу. Первый вопрос, который надо задавать человеку, делающем deepCopy — «а без deepCopy точно ничего не получится»?
WanSpi
21.06.2019 14:37Он не медленный, он самый быстрый.
В каком месте он самый быстрый? Я прошелся по тесту в Вашей ссылке, и мне показоло что он самый медленный, при этом в два раза медленее того же 'Object.assign({}, obj);'ganqqwerty
21.06.2019 14:41+1дык а Object.assign делает shallow copy, не deep copy
WanSpi
21.06.2019 19:56Согласен, но собственно способ 'JSON.parse(JSON.stringify(obj))' копирует очень малую часть от самого объекта, так что я бы не стал их сравнивать, так как одно и другое далеко от полного копирования объекта.
ganqqwerty
21.06.2019 14:56Хахаха, а вот тут уже я, не подумав, ляпнул. И впрямь, самый медленный, на jsbench 100% — это максимум, чем меньше процентов тем лучше. Как удалять комменты на хабре
ganqqwerty
21.06.2019 12:37Пункт 12 — так выглядит код говнокодера и script-kiddy, не понимающего ни что такое инкапсуляция, ни что такое предусловия. Ты взял неизвестный тебе алгоритм сортировки, который ожидает консистентную функцию сравнения (если A>B, то B<A, а если A>B и B>C, то A>C) и передал туда фигню вместо консистентной функции сравнения. Как будет работать неизвестный тебе алгоритм, если передать в него не то, что он ожидает? Завершится ли он когда-нибудь? Если завершится, то за какое время? Ах, «но ведь оно работает, я пробовал во всех браузерах?»
P.S. это не обращение к авторам статьи, это просто моя клокочущая ненависть к таким идеям.
rvs2016
21.06.2019 12:54> Приведение значений к логическому типу
> myBoolean = !!myVariable;
А зачем?
Ведь, если myVariable = 1, то
if ( myVariable ) { // я и без boolean, а на честном <s>слове</s> integer попаду в эту ветвь оператора if }
WanSpi
21.06.2019 13:16Вот тоже не знаю зачем это делать, разве что при проверке ===, хотя тоже не понятно зачем это делать, помню даже когда то спорил с одним 'специалистом', который с пеной у рта доказывал что нужно писать именно 'if (!!myVariable)', хотя так и не смог привести доказательства, когда данный пример будет отличаться от 'if (myVariable)'
JustDont
21.06.2019 13:31Так делать нужно, очевидно, когда вам нужен именно boolean (для дальнейших вычислений). Использовать это прямо в ифах — довольно бессмысленная затея, а вот «утечь» строку в код, который ожидает именно boolean (скажем, в сериализацию) — опасно.
the_o
21.06.2019 16:14+1Чаще всего подобное приведение к boolean встречается в конструкциях типа switch(true), т.к. switch как раз сравнивает строго:
const myVariable = 1; switch (true) { case myVariable: console.log('Эта ветка не отработает'); break; case !!myVariable: console.log('А эта отрабтает'); break; }
WanSpi
21.06.2019 16:38+2Как я уже писал выше, это было сказано лишь для ифа, а для строгого сравнения — да, можно такое пременить.
Но чисто для интереса хотело бы спросить, где вообще такой свитч можно применить? Не лучше для данного примера использовать обычный if/else?the_o
21.06.2019 19:35Для данного конкретного, естественно, if'а достаточно. Даже просто && обойтись можно. Это была просто демонстрация того, что switch требует строгого равенства, и если об этом забыть, можно получить неожиданный результат.
А сам switch(true) именно с необходимостью приводить к boolean я нередко наблюдал в ситуациях, подобных вот этой:
getSomeMeasurementAsync((err, data) => { switch (true) { case !!err: handleError(`Error getting measurement`, err); break; case !!data.error: handleError(`Measurement error`, data.error); break; default: handleMeasurement(data.measurement); } });
WanSpi
21.06.2019 19:45+1Задумка конечно интересная, запомню, может когда нибудь пригодиться, но даже в данном случае я бы не рекомендовал использовать данную структуру.
faiwer
21.06.2019 19:29!!variable
очень распространённый "хак". Используется когда вам нужно гарантировано привести к boolean-у и не хочется писатьvariable ? true : false
. Т.е. не вif()
,until()
,repeat()
и пр., а скорее при формировании нового объекта где по контракту должен быть boolean.WanSpi
21.06.2019 19:37Кажется я в третий раз буду писать про это :)
Я знаю про этот «хак», и тоже использую его временами, и как написали Вы, и написали выше, он очень хорошо подходит для строгого сравнения, либо же при сериализации, но я лишь писал про ифы, не более того :)faiwer
21.06.2019 19:46Есть даже линтеры чтобы
if(!!...)
не использовали :-)WanSpi
21.06.2019 19:51Вообще я не понимаю от куда пошла мода писать if(!!...), так как где то два года назад, много кто мне об этом начал говорить, хотя выводов так и не предоставляли, лишь в стиле «так надо», как будто кто то, что то ляпнул, и все как стадо начали повторять…
ganqqwerty
22.06.2019 03:04Ну а если у вас какой-нить filter, и вы не знаете, что у него внутри — строгое или нестрогое сравнение?
vvadzim
24.06.2019 13:22Ну boolean часто нужен всё-таки. Выше про сериализацию писали. Иногда надо сохранить логическое значение надолго, а прилетает всё что угодно. Приведение к boolean перед сохранением позволяет отпустить значение для GC.
Но я сам очень не люблю
и тупо пишу!!value
Boolean(value)
genew
21.06.2019 17:20+1Пункт 10. Быстрое создание числовых массивов
На MDN есть еще вот такой пример:
// Генерирования последовательности чисел Array.from({ length: 5 }, (v, k) => k); // [0, 1, 2, 3, 4]
yarkov
Пункт 2
Эту ситуацию надо объяснить, раз уж взялись давать советы.
Odrin
Поведение ожидаемое и очевидное, что здесь объяснять?
yarkov
Для кого оно ожидаемое и очевидное, тот не почерпнет в статье для себя ничего нового. Ну а впрочем это только моё мнение…
CoolCmd
то, как передаются объекты (по ссылке) — это первые главы любого учебника по JS
yarkov
Я то в курсе. Ниже вот отписался.
CoolCmd
ссылки — это азы, их в учебниках объясняют, а не в статьях, которые рассчитаны на людей, уже во всю херачащих на JS.
такие вещи, как обмен значений 2х переменных — вещь неочевидная даже для человека, который знает о destructuring assignment.
faiwer
+1, не приходило в голову, что destructuring assignment может работать и без декларации (const, var, let). Плюс я был почти уверен, что
{ ...myProperty && { propName: myProperty } }
просто упадёт с ошибкой если myProperty будет non-object (не падает).Sirion
Я это знал, но удивился, что деструктуризация работает и в следующем случае:
NOCELL
В JS как и в других языках обьекты или экзмемпляры класса сравниваются по ссылке, а не по значению.
здесь вы создали обьект.здесь вы сравниваете обьект с самим собой.
здесь вы создаете два обьекта, и соотвественно с разными ссылками.
здесь вы сравниваете ссылки на два разных обьекта.
yarkov
Да мне то Вы зачем это объясняете? Я прекрасно это знаю. Коммент о том, что тот, кому будет полезна статья, может не знать этих нюансов.
Сужу по вопросам на Тостере и в чатиках Телеграма. Частенько проскакивают попытки сделать массив уникальных объектов с помощью Set.