Комментарии 76
const obj = {a:1};
const myObjArray = [obj, obj];
const deDupeObj = [...new Set(myObjArray)];
const myArray = [{a:1}, {a:1}];
const deDupe = [...new Set(myArray)];
// Как и ожидалось
console.log(deDupeObj); // [{a:1}]
// Упс...
console.log(deDupe); // [{a:1}, {a:1}]
Эту ситуацию надо объяснить, раз уж взялись давать советы.
ссылки — это азы, их в учебниках объясняют, а не в статьях, которые рассчитаны на людей, уже во всю херачащих на JS.
Для кого оно ожидаемое и очевидное, тот не почерпнет в статье для себя ничего нового.
такие вещи, как обмен значений 2х переменных — вещь неочевидная даже для человека, который знает о destructuring assignment.
+1, не приходило в голову, что destructuring assignment может работать и без декларации (const, var, let). Плюс я был почти уверен, что { ...myProperty && { propName: myProperty } }
просто упадёт с ошибкой если myProperty будет non-object (не падает).
13: На самом деле такое копирование обладает рядом недостатков.
Эти вот «недостатки» — они не обязательно недостатки, иногда они наоборот крайне полезны. Единственное, что тут нужно иметь в виду, что прогон объектов через JSON — это всё-таки НЕ клонирование.
И, кстати, оно очень даже быстро работает; делая клонирование через обход дерева объектов руками, вполне можно сделать даже медленнее.
У вас есть стейт приложения. Пользователь в этот стейт вносит изменения, которые должны будут примениться по нажатии кнопки «применить изменения». Как будете хранить «грязный» стейт? Как дельту к чистому? И если никакие undo-redo заведомо не нужны — всё равно будете дельты хранить? И будете писать код по созданию дельт и их накатыванию, вместо того, чтоб отклонировать чистый стейт, а потом залить грязный поверх чистого (в одну строчку)?
И к слову о этой ветке комментариев, Immutable.js — это как раз таки копирование объектов во все поля. Только зашитое в либу.
Там хитрые внутренние механизмы, избегающие дублирования данных и лишней работы.
Никакая внутренняя хитрость не поможет вам сохранить два указателя на данные по той же цене, что и один указатель. Я в курсе, что immutable.js очень неплохо оптимизирован (и вообще либа концептуально хорошая), но то, что он делает — это всё равно копирование структур данных. Даже если копируется только левая сторона (указатели).
Immutable.jsЭтот тот, в котором поля указываются строками и IDE никак не может помочь с написанием кода? «Гениальное» решение.
Люди уже давно на TS переходят, а некоторые — до сих пор в динамику бросаются
Решал подобную задачу. Активно использовал иммутабельность (без immutable.js). В итоге для redo/undo не хранил никаких дельт, а хранил ссылку на предыдущий стейт целиком. Сами понимаете, вес одного undo-среза был крошечный.
Вопрос с сериализацией и десериализацией решил просто: кастомный cloneObject метод, который используя Map упаковывал state в json, где все circular references были строкой вида $circular$firstPath
. И был такой же метод десериализации который строил такой же объект восстанавливая все ссылки. На всё про всё строк 30 кода.
Хорошо вам там. В плюсах аналогичный подход почти наверняка уронит sort и прочие алгоритмы, ожидающие корректный порядок.
Можно вылезти за границы массива, например, или уйти в бесконечную рекурсию/цикл, или ещё чего натворить. Вот я сконструировал пример, который валит libstdc++, примерно с третьей попытки.
Собственно, эта страница приоткрывает завесу: требуется, чтобы предикат описывал некоторый strict weak order со всей его аксиоматикой.
На CppNow 2019 был доклад Гашпера про порядки и как они относятся к C++20'овому operator spaceship, и в лицах ряда людей читалось непонимание математической части (хотя там ничего принципиально сложного не было, хоть я и дико не согласен с философией его подхода). Но это так, навеянные мысли вслух к вопросу о том, зачем программистам математика.
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
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]])
Зато нагляднее некуда.
$qs = sub { my ($car, @cdr) = @_; @_ == () ? () : ($qs->(grep { $_ < $car} @cdr), $_[0], $qs->(grep { $_ >= $car} @cdr)) };
что здесь используется мелкое копирование свойств объектов
translate.google.com/#view=home&op=translate&sl=en&tl=ru&text=shallow%20copying
Почему-то знал, что скажет google переводчик еще до того, как ввел в него эту фразу.
JSON.parse(JSON.stringify(originalObject));
Удачи в копировании объектов с properties NaN, Infinity, Function, и многими другими.Если вы удаляете дубликаты из массива, как насчет функции `removeDuplicates(arr, predicate)`? Вы можете реализовать ее оптимальным способом, она будет фурычить быстрее, а постороннему программисту будет сразу ясно — вот тут удаляют дубликаты. Ну или в каком-нибудь lodash такая функция наверняка есть. Вы можете гарантировать, что ваш код всегда будут читать любители и знатоки однострочников? Или может быть, в вашем проекте появляются иногда джуны, или переученные за две недели с джавы «фуллстэк-девелоперы»? Сколько времени это будет стоить вашей компании на промежутке в хотя бы год? Если ваша зарплата включает в себя процент от прибылей, такие вопросы — не праздные.
Мой вывод: такие вещи могут быть полезны только в случае если вы пишете код на выброс, для себя на один раз. Как только речь о серьезных продуктах и мало-мальских командах, такое ковбойство надо вымарывать.
Во-вторых, применять этот способ нужно не когда у вас мало времени, а когда вы точно уверены, что объекты будут простенькие, без циклических ссылок или ссылок на какие-нибудь гигантские другие объекты и нужно чтобы это было супер-быстро, потому что делается миллиарды раз. Да я могу маленький движок графовой БД таким, что попытка `stringify` одного из узлов выведет полностью всю эту базу. Первый вопрос, который надо задавать человеку, делающем deepCopy — «а без deepCopy точно ничего не получится»?
Он не медленный, он самый быстрый.
В каком месте он самый быстрый? Я прошелся по тесту в Вашей ссылке, и мне показоло что он самый медленный, при этом в два раза медленее того же 'Object.assign({}, obj);'
P.S. это не обращение к авторам статьи, это просто моя клокочущая ненависть к таким идеям.
> myBoolean = !!myVariable;
А зачем?
Ведь, если myVariable = 1, то
if ( myVariable ) {
// я и без boolean, а на честном <s>слове</s> integer попаду в эту ветвь оператора if
}
const myVariable = 1;
switch (true) {
case myVariable:
console.log('Эта ветка не отработает');
break;
case !!myVariable:
console.log('А эта отрабтает');
break;
}
Но чисто для интереса хотело бы спросить, где вообще такой свитч можно применить? Не лучше для данного примера использовать обычный if/else?
Такой switch можно использовать если очень хочется не пройти code-review :)
А сам 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);
}
});
getSomeMeasurementAsync((err, data) => {
if (err) {
return handleError(`Error getting measurement`, err)
} else if (data.error) {
return handleError(`Measurement error`, data.error)
}
return handleMeasurement(data.measurement)
})
!!variable
очень распространённый "хак". Используется когда вам нужно гарантировано привести к boolean-у и не хочется писать variable ? true : false
. Т.е. не в if()
, until()
, repeat()
и пр., а скорее при формировании нового объекта где по контракту должен быть boolean.
Я знаю про этот «хак», и тоже использую его временами, и как написали Вы, и написали выше, он очень хорошо подходит для строгого сравнения, либо же при сериализации, но я лишь писал про ифы, не более того :)
Есть даже линтеры чтобы if(!!...)
не использовали :-)
Ну а если у вас какой-нить filter, и вы не знаете, что у него внутри — строгое или нестрогое сравнение?
const myObject = { ...myProperty && { propName: myProperty } }
Да ну, такое надо на ревью обратно отправлять.
А почему бы не просто "myProperty ? { propName: myProperty } : {}
"?
Вроде и понятнее, и символов меньше...
А как было бы лучше? if'ами?
Да, лучше if'ами
let config = {
value1,
value2,
}
if (condition1) {
config = { ...config, value3, value4 }
}
...
А как было бы лучше? if'ами?А ещё лучше, чтобы у вас был строго-типизированный код и наполнение объектов полями не зависело бы от if'ов
На MDN есть еще вот такой пример:
// Генерирования последовательности чисел
Array.from({ length: 5 }, (v, k) => k);
// [0, 1, 2, 3, 4]
Всегда казалось, что например написать `const myBoolean = Boolean(myVariable);` более очевидно для понимания, нежели играться с восклицательными знаками. Впрочем это актуально для любого приведения типа, будь то строка или число.
const code = Math.floor(Math.random() * Math.pow(10,6)).toString().padStart(6, "0");
Будет чище. Не придется считать нули :)
13 полезных однострочников на JavaScript