Jak usunąć obiekty z asocjacyjnej tablicy javascript?

618

Załóżmy, że mam ten kod:

var myArray = new Object();
myArray["firstname"] = "Bob";
myArray["lastname"] = "Smith";
myArray["age"] = 25;

Teraz, jeśli chcę usunąć „nazwisko”? .... czy istnieje jakiś odpowiednik
myArray["lastname"].remove()?

(Potrzebuję elementu, który zniknął, ponieważ liczba elementów jest ważna i chcę zachować porządek.)

djot
źródło
27
Wskazówka: nie myl tablic i map. Niektóre języki, takie jak php, mają jeden obiekt dla obu. Chociaż użyłeś tutaj odpowiedniego typu (new Object ()), nazwałeś go myArray, to tylko kwestia standardów dla języka.
Juan Mendes
Nie zapominaj, że JavaScript nie obsługuje typów i wszystko jest przedmiotem. Zobacz odpowiedź Saula poniżej.
stevek
4
@StephanKristyn - aby być precyzyjnym, JS ma typy, ale w sposób dynamiczny i słaby . Na przykład, chociaż jego zmienne rzeczywiście są bez typu, ich wartości nie. To jest część dynamiczna . Słaby oznacza, że operacje między różnymi typami wartości nie są ściśle określone i opierają się na konwersjach zakulisowych; na przykład "Test" + {};jest całkowicie poprawną instrukcją JS.
Saul

Odpowiedzi:

1134

Obiekty w JavaScript można traktować jako tablice asocjacyjne, odwzorowujące klucze (właściwości) na wartości.

Aby usunąć właściwość z obiektu w JavaScript, użyj deleteoperatora:

const o = { lastName: 'foo' }
o.hasOwnProperty('lastName') // true
delete o['lastName']
o.hasOwnProperty('lastName') // false

Zauważ, że po deletezastosowaniu do właściwości indeksu Arrayutworzysz rzadko zapełnianą tablicę (tj. Tablicę z brakującym indeksem).

Podczas pracy z instancjami Array, jeśli nie chcesz tworzyć słabo zaludnionej tablicy - a zazwyczaj nie - powinieneś użyć Array#splicelub Array#pop.

Pamiętaj, że deleteoperator w JavaScript nie zwalnia bezpośrednio pamięci. Jego celem jest usunięcie właściwości z obiektów. Oczywiście, jeśli usuwana właściwość zawiera jedyne zachowane odwołanie do obiektu o, to następnie obędzie usuwana śmieci w normalny sposób.

Korzystanie z deleteoperatora może wpływać na zdolność silników JavaScript do optymalizacji kodu .

Dennis C
źródło
18
Spowoduje to problemy, jeśli zostanie użyty w instancji obiektu Array w celu usunięcia istniejącego elementu, np delete myArray[0]. Zobacz stackoverflow.com/a/9973592/426379 i Usuwanie elementów tablicy
Saul
4
Jakie problemy zostaną spowodowane?
Gottox,
26
@Gottox - lengthWłaściwość obiektu Array pozostaje niezmieniona.
Saul
12
@Saul: nie będzie mieć problemy, jeśli myArraynaprawdę wykorzystywane jako tablica - ale to nie jest ( myArrayjest niefortunna nazwa), to jest obiekt. Więc w tym przypadku deletejest OK. Zauważ, że nawet gdyby został on utworzony new Array()i użyty jako tablica asocjacyjna, nadal byłby OK. Twoje ostrzeżenie jest jednak nadal ważne, jeśli używasz prawdziwych tablic.
johndodo
2
@johndodo - True. Dlatego zacząłem swój pierwszy komentarz od To spowoduje problemy, jeśli zostanie użyte w instancji obiektu Array . Niemniej jednak wolę podejście, które we wszystkich przypadkach działa poprawnie, patrz moja odpowiedź poniżej.
Saul
79

Wszystkie obiekty w JavaScript są implementowane jako tablice mieszające / tablice asocjacyjne. Zatem następujące są równoważne:

alert(myObj["SomeProperty"]);
alert(myObj.SomeProperty);

I, jak już wspomniano, „usuwasz” właściwość z obiektu za pomocą deletesłowa kluczowego, którego możesz użyć na dwa sposoby:

delete myObj["SomeProperty"];
delete myObj.SomeProperty;

Mam nadzieję, że dodatkowe informacje pomogą ...

Jason Bunting
źródło
10
należy zauważyć, że notacja kropkowa nie działa, jeśli właściwość nie jest prostym terminem. tzn. myObj['some;property']działa, ale myObj.some;propertynie (z oczywistych powodów). Również może nie być oczywiste, że można użyć zmiennej w notacji nawiasowej, tj.var x = 'SomeProperty'; alert(myObj[x])
Kip
2
„Wszystkie obiekty w JavaScript są implementowane jako tablice skrótów / tablice asocjacyjne.” - false. V8 woli przechowywać obiekt jako ukrytą klasę + gęsto upakowane pola. Tylko jeśli robisz im dziwne rzeczy (takie jak usuwanie pól), poddaje się i używa mapy skrótów za scenami.
John Dvorak,
4
@JanDvorak - hej, rozpoznajesz, kiedy ta odpowiedź została pierwotnie napisana, tak? Ten opis był i nadal jest wystarczający do większości celów. To powiedziawszy, rozumiem, że jestem żmudnie pedantyczny. :)
Jason Bunting,
41

Żadna z poprzednich odpowiedzi nie odnosi się do faktu, że JavaScript nie ma na początku tablic asocjacyjnych - nie ma takiego arraytypu, patrz typeof.

Javascript ma instancje obiektów o właściwościach dynamicznych. Gdy właściwości zostaną pomylone z elementami instancji obiektu Array, wówczas nastąpi sytuacja Bad Things ™:

Problem

var elements = new Array()

elements.push(document.getElementsByTagName("head")[0])
elements.push(document.getElementsByTagName("title")[0])
elements["prop"] = document.getElementsByTagName("body")[0]

console.log("number of elements: ", elements.length)   // returns 2
delete elements[1]
console.log("number of elements: ", elements.length)   // returns 2 (?!)

for (var i = 0; i < elements.length; i++)
{
   // uh-oh... throws a TypeError when i == 1
   elements[i].onmouseover = function () { window.alert("Over It.")}
   console.log("success at index: ", i)
}

Rozwiązanie

Aby mieć uniwersalną funkcję usuwania, która cię nie wysadzi, użyj:

Object.prototype.removeItem = function (key) {
   if (!this.hasOwnProperty(key))
      return
   if (isNaN(parseInt(key)) || !(this instanceof Array))
      delete this[key]
   else
      this.splice(key, 1)
};

//
// Code sample.
//
var elements = new Array()

elements.push(document.getElementsByTagName("head")[0])
elements.push(document.getElementsByTagName("title")[0])
elements["prop"] = document.getElementsByTagName("body")[0]

console.log(elements.length)                        // returns 2
elements.removeItem("prop")
elements.removeItem(0)
console.log(elements.hasOwnProperty("prop"))        // returns false as it should
console.log(elements.length)                        // returns 1 as it should
Saul
źródło
8
To rozwiązanie ma dwa problemy: ukrywa fakt, że tablice i obiekty są całkowicie różnymi zwierzętami w JS (znasz to, ale najwyraźniej OP nie) i używa prototypów. OP byłoby lepiej, gdyby dowiedział się o tablicach i obiektach (i odpowiednio nazwałby swoje zmienne) - próba ukrycia różnic między nimi sprawi mu więcej kłopotów. IMHO oczywiście.
johndodo
1
@ johndodo - wszystkie ArrayJS są obiektami, spróbuj typeof new Array();lub typeof []zweryfikuj. Arrayjest po prostu pewnym rodzajem przedmiotu, a wcale nie „inną bestią”. W JS obiekty wyróżniają się nazwą konstruktora i łańcuchem prototypów, patrz Programowanie oparte na prototypach .
Saul
9
Brakuje Ci sensu. Wiem, że tablice też są obiektami, ale to nie znaczy, że mądrze jest ich używać jako takich. Programista powinien zdecydować, czy chce użyć czegoś jako tablicy (z push, pop, [], ...) czy jako obiekt / „tablica asocjacyjna”. Mieszanie i łączenie nie jest dobrym przepisem, właśnie z powodu problemów, które twoje rozwiązanie próbuje ukryć. Jeśli wcześniej zdecydujesz, który wzór projektowy zastosować (tablicę lub obiekt), nie będzie takich problemów.
johndodo
7
@johndodo - O jakich konkretnie problemach mówisz? Celem powyższego kodu jest zajęcie się problemem deleteoperatora niedoborów Arraypoprzez zapewnienie prostej funkcji polimorficznej.
Saul
1
deletenie ma niedoboru. deletesłuży do usuwania właściwości. Otóż ​​to. Zastosowanie operatora delete do indeksu tablicy usuwa ten indeks. Czego więcej potrzeba? Pozostaje ci rzadka tablica, która jest cechą języka. Jeśli nie chcesz rzadkiej tablicy, nie usuwaj indeksu: użyj splicelub pop.
Ben Aston
35

To tylko usuwa usuwa obiekt, ale nadal utrzymuje tę samą długość tablicy.

Aby usunąć, musisz zrobić coś takiego:

array.splice(index, 1);
Bipin
źródło
11
Rzeczywiście, ale w tym przypadku tablica nie jest używana, tylko zwykły stary obiekt, dlatego nie ma metody długości ani łączenia.
MooGoo,
2
@Andreaa Panagiotidis wyjątkiem sytuacji, gdy nie mówimy o tablic, w tym przypadku to jest złe 100% czasu 🙂
Drenajów
17

Chociaż przyjęta odpowiedź jest poprawna, brakuje wyjaśnienia, dlaczego działa.

Po pierwsze, twój kod powinien odzwierciedlać fakt, że NIE jest to tablica:

var myObject = new Object();
myObject["firstname"] = "Bob";
myObject["lastname"] = "Smith";
myObject["age"] = 25;

Pamiętaj, że wszystkie obiekty (w tym Arrays) mogą być używane w ten sposób. Nie należy jednak oczekiwać, że standardowe funkcje tablic JS (pop, push, ...) będą działać na obiektach!

Jak powiedziano w zaakceptowanej odpowiedzi, możesz następnie użyć deletedo usunięcia wpisów z obiektów:

delete myObject["lastname"]

Powinieneś zdecydować, którą trasę wybrać - albo użyj obiektów (tablice asocjacyjne / słowniki), albo tablic (mapy). Nigdy nie mieszaj ich dwóch.

johndodo
źródło
6
Bardzo dobra odpowiedź. Radziłbym tylko każdemu, kto to czyta, aby tablice w javascript nie były abstrakcyjne jako „mapy”, a raczej „listy”. Jest tak, ponieważ nie powinieneś próbować kontrolować indeksu elementów podczas korzystania z tablic. Jeśli spróbujesz ... cóż, po prostu nie rób: D
rodolfo42
6

Użyj metody, spliceaby całkowicie usunąć element z tablicy obiektów:

Object.prototype.removeItem = function (key, value) {
    if (value == undefined)
        return;

    for (var i in this) {
        if (this[i][key] == value) {
            this.splice(i, 1);
        }
    }
};

var collection = [
    { id: "5f299a5d-7793-47be-a827-bca227dbef95", title: "one" },
    { id: "87353080-8f49-46b9-9281-162a41ddb8df", title: "two" },
    { id: "a1af832c-9028-4690-9793-d623ecc75a95", title: "three" }
];

collection.removeItem("id", "87353080-8f49-46b9-9281-162a41ddb8df");
HarpyWar
źródło
1
jest to bardziej ogólne rozwiązanie, które można dodać do pliku js, a metoda będzie dostępna dla wszystkich tablic, nie tylko jednej tablicy.
Hussain
6

Jak zauważyły ​​inne odpowiedzi, nie używasz tablicy JavaScript, ale obiekt Javascript, który działa prawie jak tablica asocjacyjna w innych językach, z wyjątkiem tego, że wszystkie klucze są konwertowane na ciągi. Nowa mapa przechowuje klucze w oryginalnym typie.

Jeśli masz tablicę, a nie obiekt, możesz użyć funkcji .filter tablicy , aby zwrócić nową tablicę bez elementu, który chcesz usunąć:

var myArray = ['Bob', 'Smith', 25];
myArray = myArray.filter(function(item) {
    return item !== 'Smith';
});

Jeśli masz starszą przeglądarkę i jQuery, jQuery ma $.grepmetodę, która działa podobnie:

myArray = $.grep(myArray, function(item) {
    return item !== 'Smith';
});
Dzeimsas Zvirblis
źródło
doskonałe wyjaśnienie. Użyłem filtra, aby osiągnąć pożądany efekt. Czy możesz wyjaśnić, jak działa element zwracany, aby usunąć obiekt z tablicy. Zakładam, że zwraca tablicę, o ile nie zawiera uwzględnionego ciągu.
Edward
5

Używasz Object, na początku nie masz tablicy asocjacyjnej. W przypadku tablicy asocjacyjnej dodawanie i usuwanie elementów przebiega następująco:

    Array.prototype.contains = function(obj) 
    {
        var i = this.length;
        while (i--) 
        {
            if (this[i] === obj) 
            {
                return true;
            }
        }
        return false;
    }


    Array.prototype.add = function(key, value) 
    {
        if(this.contains(key))
            this[key] = value;
        else
        {
            this.push(key);
            this[key] = value;
        }
    }


    Array.prototype.remove = function(key) 
    {
        for(var i = 0; i < this.length; ++i)
        {
            if(this[i] == key)
            {
                this.splice(i, 1);
                return;
            }
        }
    }



    // Read a page's GET URL variables and return them as an associative array.
    function getUrlVars()
    {
        var vars = [], hash;
        var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');

        for(var i = 0; i < hashes.length; i++)
        {
            hash = hashes[i].split('=');
            vars.push(hash[0]);
            vars[hash[0]] = hash[1];
        }

        return vars;
    }



    function ForwardAndHideVariables() {
        var dictParameters = getUrlVars();

        dictParameters.add("mno", "pqr");
        dictParameters.add("mno", "stfu");

        dictParameters.remove("mno");



        for(var i = 0; i < dictParameters.length; i++)
        {
            var key = dictParameters[i];
            var value = dictParameters[key];
            alert(key + "=" + value);
        }
        // And now forward with HTTP-POST
        aa_post_to_url("Default.aspx", dictParameters);
    }


    function aa_post_to_url(path, params, method) {
        method = method || "post";

        var form = document.createElement("form");

        //move the submit function to another variable
        //so that it doesn't get written over if a parameter name is 'submit'
        form._submit_function_ = form.submit;

        form.setAttribute("method", method);
        form.setAttribute("action", path);

        for(var i = 0; i < params.length; i++)
        {
            var key = params[i];

            var hiddenField = document.createElement("input");
            hiddenField.setAttribute("type", "hidden");
            hiddenField.setAttribute("name", key);
            hiddenField.setAttribute("value", params[key]);

            form.appendChild(hiddenField);
        }

        document.body.appendChild(form);
        form._submit_function_(); //call the renamed function
    }
Stefan Steiger
źródło
4

Możesz usunąć wpis z mapy, przypisując go jawnie do „niezdefiniowanego”. Tak jak w twoim przypadku:

myArray ["lastname"] = undefined;

Amytis
źródło
Może to być przydatne w przypadkach, gdy nie ma pewności, czy klucz istnieje w słowniku, ale chce go zdezynfekować, jeśli tak jest. Popraw mnie, jeśli się mylę, Amytis.
Hassan Baig
3

Jeśli z jakiegoś powodu klawisz usuwania nie działa (jakby dla mnie nie działał)

Możesz go podzielić, a następnie odfiltrować niezdefiniowane wartości

// to cut out one element via arr.splice(indexToRemove, numberToRemove);
array.splice(key, 1)
array.filter(function(n){return n});

Nie próbuj ich łączyć w łańcuch, ponieważ połączenie zwraca usunięte elementy;

Leon
źródło
3

Możemy również użyć go jako funkcji. Angular generuje pewien błąd, jeśli jest używany jako prototyp. Dzięki @HarpyWar. Pomogło mi to rozwiązać problem.

var removeItem = function (object, key, value) {
    if (value == undefined)
        return;

    for (var i in object) {
        if (object[i][key] == value) {
            object.splice(i, 1);
        }
    }
};

var collection = [
    { id: "5f299a5d-7793-47be-a827-bca227dbef95", title: "one" },
    { id: "87353080-8f49-46b9-9281-162a41ddb8df", title: "two" },
    { id: "a1af832c-9028-4690-9793-d623ecc75a95", title: "three" }
];

removeItem(collection, "id", "87353080-8f49-46b9-9281-162a41ddb8df");
Omkar Kamale
źródło
2

To bardzo proste, jeśli masz w projekcie zależność od underscore.js -

_.omit(myArray, "lastname")
vatsal
źródło
1

Użycie "delete"słowa kluczowego spowoduje usunięcie elementu tablicy z tablicy w javascript.

Na przykład,

Rozważ następujące stwierdzenia.

var arrayElementToDelete = new Object();

arrayElementToDelete["id"]           = "XERTYB00G1"; 
arrayElementToDelete["first_name"]   = "Employee_one";
arrayElementToDelete["status"]       = "Active"; 

delete arrayElementToDelete["status"];

Ostatni wiersz kodu usunie element tablicy, którego kluczem jest „status” z tablicy.

Ravindra Miyani
źródło
0
var myArray = newmyArray = new Object(); 
myArray["firstname"] = "Bob";
myArray["lastname"] = "Smith";
myArray["age"] = 25;

var s = JSON.stringify(myArray);

s.replace(/"lastname[^,}]+,/g,'');
newmyArray = JSON.parse(p);

Bez zapętlania / iteracji otrzymujemy ten sam wynik

Lalith Kumar
źródło
0

W przypadku „tablic”:

Jeśli znasz indeks:

array.splice(index, 1);

Jeśli znasz wartość:

function removeItem(array, value) {
    var index = array.indexOf(value);
    if (index > -1) {
        array.splice(index, 1);
    }
    return array;
}

Najbardziej pozytywna odpowiedź na pytanie deletedziała dobrze w przypadku obiektów, ale nie w przypadku prawdziwych tablic. Jeśli deletego użyję, usuwa elementy z pętli, ale utrzymuje element jako, emptya długość tablicy nie zmieni się. Może to stanowić problem w niektórych scenariuszach.

Na przykład, jeśli wykonam myArray.toString () na myArray po usunięciu za deletejego pośrednictwem , utworzy pusty wpis tj. ,,

Arvind K.
źródło
0

Jedyna dla mnie metoda działania:

function removeItem (array, value) {
    var i = 0;
    while (i < array.length) {
        if(array[i] === value) {
            array.splice(i, 1);
        } else {
            ++i;
        }
    }
    return array;
}

stosowanie:

var new = removeItem( ["apple","banana", "orange"],  "apple");
// ---> ["banana", "orange"]
T.Todua
źródło
Dlaczego zamiast tego nie użyć filtra? jest to idealny przypadek użycia filtra
Code Maniac