Nano Hash - криптовалюты, майнинг, программирование

Самый эффективный способ добавить значение в массив

Предполагая, что у меня есть массив размером N (где N > 0), есть ли более эффективный способ добавления к массиву, который не требует O (N + 1) шагов?

В коде, по сути, то, что я сейчас делаю, это

function prependArray(value, oldArray) {
  var newArray = new Array(value);

  for(var i = 0; i < oldArray.length; ++i) {
    newArray.push(oldArray[i]);
  }

  return newArray;
}

  • как бы мне ни нравились связанные списки и указатели, я чувствую, что должен быть более эффективный способ делать что-то с использованием собственных типов данных JS 01.06.2011
  • @samccone: Да, не обращайте внимания на мой комментарий, извините, я думал, вы сказали Java: P 01.06.2011
  • Java, JavaScript, C или Python, не имеет значения, на каком языке: компромисс сложности между массивами и связанными списками одинаков. Связанные списки, вероятно, довольно громоздки в JS, потому что для них нет встроенного класса (в отличие от Java), но если вам действительно нужно время вставки O (1), то вам действительно нужен связанный список. 01.06.2011
  • Требуется ли его клонировать? 01.06.2011
  • Если требуется клонировать его, то unshift неуместен, поскольку он изменяет исходный массив, а не создает копию. 01.06.2011

Ответы:


1

Я не уверен, что это более эффективно с точки зрения большого О, но, конечно, использование метода unshift более кратко:

var a = [1, 2, 3, 4];
a.unshift(0);
a; // => [0, 1, 2, 3, 4]

[Изменить]

Этот тест jsPerf показывает, что unshift работает значительно быстрее по крайней мере в нескольких браузерах, независимо от возможно, другая производительность big-O если вы согласны с изменением массива на месте. Если вы действительно не можете изменить исходный массив, вы должны сделать что-то вроде приведенного ниже фрагмента, который, похоже, не намного быстрее, чем ваше решение:

a.slice().unshift(0); // Use "slice" to avoid mutating "a".

[Изменить 2]

Для полноты картины вместо примера OP prependArray(...) можно использовать следующую функцию, чтобы воспользоваться преимуществом метода Array unshift(...):

function prepend(value, array) {
  var newArray = array.slice();
  newArray.unshift(value);
  return newArray;
}

var x = [1, 2, 3];
var y = prepend(0, x);
y; // => [0, 1, 2, 3];
x; // => [1, 2, 3];
01.06.2011
  • ах, бинго, должно быть, забыл об этом, мне интересно, как оно соотносится с эффективностью по сравнению с тем, что я делал 01.06.2011
  • Я уверен, что unshift по-прежнему O (N) из-за хранения массивов. Но да, это было бы намного эффективнее петли. 01.06.2011
  • Кто решил позвонить prepend unshift? 08.08.2013
  • @ScottStafford unshift звучит более подходящим для такой операции с массивом (он перемещает элементы ... более или менее физически). prepend было бы более подходящим для связанных списков, где вы буквально добавляете элементы. 22.08.2013
  • unshift - дополнительная функция к shift. Было бы странно назвать это prepend. Unshift - это сдвиг, как толчок - треск. 01.11.2013
  • Только одна проблема с этим решением. Разве unshift () не возвращает его длину, а не массив, как в этом ответе? w3schools.com/jsref/jsref_unshift.asp 08.03.2015
  • Как сказал @iDVB - вам нужно назначить нарезанный массив, чтобы иметь к нему доступ после unshift. И поэтому a.slice(0).unshift(0) не имеет смысла. Исправлено в ответе. 09.11.2015
  • push и unshift оба содержат u, тогда как pop и shift не содержат. 07.04.2016
  • этот ответ неверен. он выполняет 2 операции копирования. src.slice(0) копировать 1 src.unshift(value) копировать каждый элемент более чем на 1 слот. самый эффективный способ добавить в начало без изменения оригинала - скопировать за 1 проход, например var dest = new Array(src.length + 1); for(var i = 0; src.length > i; i++) dest[i+1] = src[i] 04.03.2017

  • 2

    В ES6 теперь вы можете использовать оператор распространения, чтобы создать новый массив с новыми элементами, вставленными перед исходными элементами.

    // Prepend a single item.
    const a = [1, 2, 3];
    console.log([0, ...a]);

    // Prepend an array.
    const a = [2, 3];
    const b = [0, 1];
    console.log([...b, ...a]);

    Обновление 2018-08-17: Производительность

    Я хотел, чтобы этот ответ представил альтернативный синтаксис, который я считаю более запоминающимся и кратким. Следует отметить, что согласно некоторым тестам (см. этот другой ответ) этот синтаксис значительно медленнее. Это, вероятно, не имеет значения, если вы не выполняете многие из этих операций в цикле.

    18.07.2016
  • Это должно быть проголосовано с 2017 года. Это кратко. Используя распространение, он возвращает новый поток, который может быть полезен для цепочки дальнейших изменений. Это также чисто (это означает, что a и b не затронуты) 30.07.2017
  • Вы не упомянули о затраченном времени по сравнению с unshift 17.08.2018
  • Спасибо @ShashankVivek. Я хотел, чтобы этот ответ представил альтернативный синтаксис, который я считаю более запоминающимся и кратким. Буду обновлять. 17.08.2018
  • @FrankTan: Ура :) +1 18.08.2018

  • 3

    Если вы добавляете массив перед другим массивом, более эффективно просто использовать concat. Так:

    const newArray = [1, 2, 3].concat([4, 5]);
    newArray; // [1, 2, 3, 4, 5]
    

    Но это все равно будет O (N) размером oldArray. Тем не менее, это более эффективно, чем итерация по oldArray вручную. Кроме того, в зависимости от деталей, это может помочь вам, потому что, если вы собираетесь добавить много значений, лучше сначала поместить их в массив, а затем объединить oldArray в конце, а не добавлять каждое из них по отдельности.

    Нет способа добиться большего, чем O (N), в размере oldArray, потому что массивы хранятся в непрерывной памяти с первым элементом в фиксированной позиции. Если вы хотите вставить перед первым элементом, вам нужно переместить все остальные элементы. Если вам нужен способ обойти это, сделайте то, что сказал @GWW, и используйте связанный список или другую структуру данных.

    01.06.2011
  • Ах да, я забыл про unshift. Но обратите внимание, что а) который изменяет oldArray, тогда как concat - нет (так что какой из них лучше для вас, зависит от ситуации), и б) он вставляет только один элемент. 01.06.2011
  • Вау, это намного медленнее. Как я уже сказал, он делает копию массива (а также создает новый массив [0]), тогда как unshift изменяет его на месте. Но оба должны быть O (N). Также приветствую ссылку на этот сайт - выглядит очень удобно. 01.06.2011
  • это хорошо для oneliner, так как concat возвращает массив, а unshift возвращает новую длину 04.05.2018

  • 4

    Если вы хотите добавить массив (a1 к массиву a2), вы можете использовать следующее:

    var a1 = [1, 2];
    var a2 = [3, 4];
    Array.prototype.unshift.apply(a1, a2);
    console.log(a1);
    // => [3, 4, 1, 2]
    
    25.12.2014

    5

    Если вам нужно сохранить старый массив, нарежьте старый и переместите новое значение (я) в начало среза.

    var oldA=[4,5,6];
    newA=oldA.slice(0);
    newA.unshift(1,2,3)
    
    oldA+'\n'+newA
    
    /*  returned value:
    4,5,6
    1,2,3,4,5,6
    */
    
    01.06.2011

    6

    Вызов unshift возвращает только длину нового массива. Итак, чтобы добавить элемент в начало и вернуть новый массив, я сделал следующее:

    let newVal = 'someValue';
    let array = ['hello', 'world'];
    [ newVal ].concat(array);
    

    или просто с оператором распространения:

    [ newVal, ...array ]
    

    Таким образом, исходный массив остается нетронутым.

    20.05.2019

    7

    У меня есть свежие тесты различных методов препендинга. Для небольших массивов (‹1000 элементов) выноска предназначена для цикла с методом проталкивания. Для огромных массивов лидером становится метод Unshift.

    Но такая ситуация актуальна только для браузера Chrome. В Firefox unshift имеет отличную оптимизацию и работает быстрее во всех случаях.

    Распространение ES6 во всех браузерах в 100+ раз медленнее.

    https://jsbench.me/cgjfc79bgx/1

    29.03.2018
  • Отличный ответ! Но чтобы убедиться, что вы не потеряете его часть, вы можете воспроизвести здесь свои тестовые примеры (возможно, с несколькими результатами выполнения образцов) на случай, например, jsbench. 27.12.2018

  • 8

    Есть специальный метод:

    a.unshift(value);
    

    Но если вы хотите добавить несколько элементов в массив, будет быстрее использовать такой метод:

    var a = [1, 2, 3],
        b = [4, 5];
    
    function prependArray(a, b) {
        var args = b;
        args.unshift(0);
        args.unshift(0);
        Array.prototype.splice.apply(a, args);
    }
    
    prependArray(a, b);
    console.log(a); // -> [4, 5, 1, 2, 3]
    
    01.06.2011
  • Нет ... unshift добавит массив в качестве первого аргумента: var a = [4, 5]; a.unshift ([1,2,3]); console.log (а); // - ›[[4, 5], 1, 2, 3] 01.06.2011
  • Ты прав! Вам нужно будет использовать Array.prototype.unshift.apply(a,b); 01.06.2011

  • 9

    Пример добавления на место:

    var A = [7,8,9]
    var B = [1,2,3]
    
    A.unshift(...B)
    
    console.log(A) // [1,2,3,7,8,9]

    16.01.2019
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..