Pull to refresh

Comments 20

UFO just landed and posted this here
Да... Я б с радостью его сделал, но не знаю как :( Не подскажете вариант?
UFO just landed and posted this here
Спасибо, сейчас поправлю
для больших массивов функция unique будет работать небыстро.
я обычно делаю так:

Array.prototype.unique = function() {
    var res = [];
    var already = {};

    for (var i = 0; i < this.length; i++) {
        var val = this[i];

        if (typeof(already['z'+val]) == 'undefined') {
            res.push(val);
            already['z'+val] = true;
        }
    }

    return res;
}

PS. 'z'+val а не просто val для корректной работы при случае, когда к Object-у добавлены новые методы через prototype.
Согласен, этот вариант тоже хорош, мне он даже несколько больше нравится :)
Догадайтесь что вернет ваша функция в таком случае

[{val:1},{val:2},{val:3}].unique();

еще пример

[1,'1',[1,2,3],'1,2,3'].unique();
оно не работает для таких случаев.
но мне кажется что эти случаи искуственно придуманы, и в реальности будут встречатся несравненно редко (по крайней мере если мне надо было делать юник, то все объекты были однородными (да, и у всех был метод наподобие get_id или get_value, чтобы получать одно ключевое поле))

Array.prototype.unique = function () {
var obj = new Object();
var elem;
for (let i = 0; elem = this[i]; ++i) {obj[elem] = 1};
var arr = [];
for (elem in obj) {arr.push(elem)};
return arr;
}
ваш метод почти аналогичен моему, и обладает теми же недостатками что и мой :)
а кроме того если в Object добавлены новые методы через prototype (так любит делать одноимённая библиотека), то будет не верный результат.

например:

Object.prototype.new_method_1 = function() { /* do something */ }
Object.prototype.new_method_2 = function() { /* do something */ }

arr = [1, 2, 3, 1, 1, 2];
alert(arr.unique());

for(j=0;j<=this.length-1;j++)

Все нормальный программисты пишут так :)

for(var j = 0; j < this.length; j++)

В вашем случае j станет глобальной, что уже не хорошо. И одна лишняя операция вычитания. К тому же строка '123' и число 123 у вас будут считаться одним и тем же, что не совсем верно - лучше использовать оператор === (эквивалентно) вместо == (равно). Ваш contains работает похоже с методом indexOf, который вошел в JS в 1.5 (или 1.6, все время путаю) и поддерживается многими современными браузерами. Только в случае с indexOf он возвращает индекс где находится найденный элемент, или -1 если элемент не найден, что дает больше возможностей и более широкое применение.
По поводу unique. Реализация самая простая, асимптотика одна и плохих N^2/2 (N квадрат пополам). Плюс издержки на вызов метода/функции (contains). Перепишите с вложенным массивом будет прирост производительности. Перепишите алгоритм, например можно отсортировать массив, а потом "схлопнуть" дубликаты - получим N*log(N), что значительно лучше (единственное что могут быть проблемы с объектами, но это тоже можно решить).
Спасибо большое за дельное предложение. Плюсов вам в карму :)
Эта реализация написана достаточно быстро, фактически, на скорую руку, так что особой производительности я от нее не ожидал. Я обязательно воспользуюсь вашим советом, перепишу функцию и снова выложу на оценку :)
ну и добавить, что вычитание быстрее,
поэтому есть смысл в for(var j=this.length-1;j>=0;j--)

Еще примеры реализации указанных ф-ий (взято с brainjacked.com):

Array.prototype.indexOf = function(element) {
var i = this.length;
for(i;i>-1 && element != this[i];i--);
return i;
}

Array.prototype.distinct = function() {
for(var i=0;i<this.length;i++)
for(var j=(i+1);j<this.length;j++)
if(this[i]==this[j])
this.splice(j--,1);

return this;
}
Да, можно по всякому написать - я написал самую распространенную == привычную форму.
По тексту indexOf
1. Это не работает. Тесты:

[].indexOf(); // вернет 0, должен -1
[].indexOf(null); // вернет 0, должен -1
[1,2].indexOf(); // вернет 2, должен -1
[1,2].indexOf(null); // вернет 2, должен -1
[undefined].indexOf(); // вернет 1, должен 0
[1,2,3,2].indexOf(2); // вернет 3, должен 1; в стандарте прописано что
возвращаемый индекс должен быть с начала массива (если не указан необязательный аргумент offset, который тут попросту опустили)
[1,0,2].indexOf(false); // вернет 1, должен -1; я искал false, а не ноль
[1,0,2].indexOf(''); // вернет 1, должен -1; я искал пустую строку, а не ноль
[1,'',2].indexOf(0); // вернет 1, должен -1; я искал ноль, а не пустую строку
и т.д.

2. Если уж писать кратко (и не обращать на то что функция в корне работает неверно) то, можно записать так:

Array.prototype.indexOf = function(element) {
for(var i = this.length; --i > -1 && element !== this[i];);
return i;
}
// я все таки добавил пару маленьких фиксов


Ну и distinct просто ужасна! Использование splice очень негативно скажется на скорости, здесь ее использование очень дорого. И в итоге получаем тот же N^2/2 и сдается мне что даже хуже.
А в prototype есть готовое решение - Array.unuq(), хотя и не очень шустрое
Там тот же N^2/2 (N квадрат пополам), и куча накладных расходов. В принципе там можно самому отсортировать массив, и передать в метод что массив отсортирован, тогда будет логарифм (собственно я и рекомендовал такой подход в предыдущем комментарии). Но все равно накладные расходы большие.
Нужно было не в мануале jQuery искать, а в коде. $.unique(array)
А добвалять новые методы ни массиву, ни объекту не советуют.
Эм... Благодарю, возможно и впрямь проглядел
Sign up to leave a comment.

Articles