2011-01-06 20 views
5

Mam obiektu takich jak:zmniejszyć duplikat w JavaScript Object

{ 
    a : 'foo', 
    b : 'bar', 
    c : 'foo', 
    d : 'baz', 
    e : 'bar' 
} 

chcę zredukować duplikaty jak:

{ 
    ac : 'foo', 
    be : 'bar', 
    d : 'baz' 
} 

Co to dobry sposób to zrobić?

Kilka zastrzeżenia:

  • Tam być tylko niewielka liczba par. (Obecnie jest ich 7, mogę sobie wyobrazić, że będzie to, powiedzmy, 20.)
  • Początkowe nazwy właściwości będą zawsze pojedynczym znakiem, na przykład w przykładzie
  • Wartości mogą potencjalnie zawierać kilkaset znaków .
  • zarówno prędkość, jak i długość kodu są bardzo ważne, ale biorąc pod uwagę niewielką liczbę wierszy, przejrzystość kodu jest prawdopodobnie nadal najważniejsza.

Odpowiedz

0

przejść przez każdy właściwości obiektu i konstruowania innego obiektu, w którym klawisze są wartości pierwszego i wartości są listy kluczy (od początku). Następnie przechodzisz przez ten drugi przedmiot i osiągasz końcowy rezultat.

coś takiego:

function noDupes(obj) { 
    var o2 = {}; 
    for (var k in obj) { 
    if (obj.hasOwnProperty(k)) { 
     var list = o2[obj[k]] || []; 
     list.push(k); 
     o2[obj[k]] = list; 
    } 
    } 
    var rv = {}; 
    for (k in o2) { 
    if (o2.hasOwnProperty(k)) 
     rv[o2[k].join('')] = k; 
    } 
    return rv; 
} 

Teraz, jeśli wartości oryginalnego obiektu nie są łańcuchami, a potem robi się bardziej zaangażować: tylko łańcuchy mogą być klucze nieruchomości w obiektu JavaScript. W takim przypadku możesz rozejrzeć się za bardziej ogólną implementacją skrótu. Jeśli twoje obiekty wydają się być dość małe (mniej niż 10 lub więcej właściwości), możesz napisać wersję n, w której po prostu iterujesz po właściwościach, a następnie powtórzysz po kolei dla każdego z nich. Prawdopodobnie byłby to zły pomysł, jeśli twoje obiekty mogą być duże i musisz wykonać tę operację bardzo często.

+0

Po prostu wr pomysł. Ale co się stanie, jeśli jedna z wartości nie jest łańcuchem? – sprugman

+0

@sprugman ah well, który sprawia, że ​​rzeczy nieco bardziej zaangażowane ... – Pointy

+0

Tak, po przetestowaniu, jeśli obiekt ma wartość, kończy się wiersz wyników, taki jak {x: "[object Object]"} – sprugman

1

Bez biblioteki o wyższych parametrach po prostu zapętlaj każdą parę (użyj hasOwnProperty) i dodaj/dodaj klucz do histogramu, w którym klucz histogramu jest wartością pary, a wartości histogramu są połączonymi kluczami. Następnie odwróć klucz/wartości histogramu.

Edit: Jeśli wartości początkowe nie są ciągi (a nie map odwracalnie) następnie istniejącą bibliotekę „hash tożsamość” może jeszcze włączyć powyższą podejście do pracy.

Alternatywnie można odwzorować, aby powiedzieć, [[k,v],...] i posortować, a następnie zastosować podejście podobne do bucket sort (wyobraź sobie, że jest już posortowane), aby scalić wartości "równych kluczy" w przejściu wyjściowym.

może iść tak (natomiast kod może mieć błędy, podejście jest dźwięk - będzie również współpracować z dowolnymi przedmiotami jak wartości tak długo, jak masz sposób na porównanie wartości):

var _f = [] 
for (var k in map) { 
    if (map.hasOwnProperty(k)) { 
    _f.push({k: k, v: map[k]}) 
    } 
} 
// you could also sort on name (a.k), if it's important 
// this makes it more versatile and deterministic in output 
// ordering than the histogram method above 
var f = _f.sort(function (a, b) { return a.v < b.v ? 1 : a.v > b.v ? -1 : 0 }) 

var res = {} 
var prev 
var name = "" 
// after the sort all {k:,v:} objects with the same values will be grouped 
// together so we only need to detect the change to the next value 
// and everything prior to that gets the merged key 
for (var i = 0; i < f.length; i++) { 
    var p = f[i] 
    if (prev != p.v && name) { 
    res[name] = prev 
    name = "" 
    } else { 
    name = name + p.k 
    } 
    prev = p.v 
} 
if (name) { // don't forget last set of values 
    res[name] = prev 
} 

// have res 
+0

Myślę, że mogę potrzebować przykład kodu, chyba że masz na myśli to samo co Pointy. – sprugman

+0

@sprugman 100% nie testowanych próbek kodu zamieszczonych ;-) –

+0

Postanowiłem, że zawsze będą ciągi, więc mogę użyć prostszej wersji, ale dzięki za to. BTW, w linii 4, myślę, że masz na myśli '_f.push()'. – sprugman

1
var Reduce = function(obj) 
{ 
    var temp = {}; 
    var val = ""; 

    for (var prop in obj) 
    { 
    val = obj[prop]; 
    if (temp[val]) 
     temp[val] = temp[val] + prop.toString(); 
    else 
     temp[val] = prop.toString(); 
    } 

    var temp2 = {}; 

    for (var prop in temp) 
    { 
    val = temp[prop]; 
    temp2[val] = prop.toString(); 
    } 

    return temp2; 
}; 

Użyj jako:

var obj = { 
    a :"foo", 
    b : "bar", 
    c : "foo", 
    d : "bar", 
    e : "bar" 
}; 

var ob2 = Reduce(obj); 
+0

Ten jest zasadniczo taki sam jak Pointy. Podoba mi się twoja, ponieważ jest klarowna, ale jego użytek ma OWNProperty i był pierwszy, więc dałem mu czek. W każdym razie dzięki. – sprugman

1

jest to najkrótsza mogę dostać go:

var obj, newObj = {}; // obj is your original 
for (var i in obj) { 
    if (!obj.hasOwnProperty(i)) continue; 
    for (var j in newObj) { 
     if (newObj.hasOwnProperty(j) && newObj[j] === obj[i]) break; 
     j = ""; 
    } 
    newObj[i + j] = obj[i]; 
    j && delete newObj[j]; 
} 

Objaśnienie:

  • To pętle przez każdego elementu oryginalnego obiektu, obj i tworzy nowy obiekt, newObj.
  • Dla każdej pozycji w oryginale, szuka półprodukcji newObj dla tej samej wartości. - Wynik to j, nazwa właściwości, jeśli została znaleziona, lub pusty łańcuch, jeśli nie.
  • W obu przypadkach nowy obiekt musi mieć właściwość o tej samej nazwie, co bieżąca właściwość w oryginalnym obiekcie, plus ta wartość: j.
  • Usuwa również znalezioną właściwość w newObj, jeśli taka istnieje, aby zapobiec tworzeniu duplikatów.

Prawdą jest, że ustawienie j = "" w pętli jest nieefektywne. Można to łatwo zastąpić drugą zmienną ustawioną na "" początkowo i j tylko wtedy, gdy znaleziono dopasowanie. Postanowiłem jednak pójść na prostotę.

+0

Zdecydowałem, że zawsze będą ciągami, więc mogę użyć prostszego rozwiązania O (n). Dzięki za to. – sprugman

0

Przebacz mi, jeśli jestem kompletnie na zewnątrz, ale wydaje mi się, że sposób, w jaki je kombinujesz, ma błędne wskazówki i wartości. A co z tym?

{ 
    'foo': ['a', 'c'], 
    'bar': ['b', 'e'], 
    'baz': ['d'] 
} 

Powinien być na tyle łatwe do konwersji:

flippedObj = {}; 
for (var letter in obj) { 
    if (obj.hasOwnProperty(letter)) { 
     var letters = flippedObj[obj[letter]]; 
     if (letters === undefined) { 
      letters = []; 
      flippedObj[obj[letter]] = letters; 
     } 

     letters.push(letter); 
    } 
} 

(. Brain-skompilowany, nie może być kilka błędów)

+0

Mam to, co muszę zacząć i chcę to, co chcę zakończyć. :) – sprugman

+0

sprugman: Uczciwa. Nie mamy dużego kontekstu, więc pomyślałem, że zaoferuję alternatywne rozwiązanie. –

0

zaczynać się zmniejszyć, który korzysta ze słownika policzyć tagi w odwrotny sposób. Bardzo wydajnych sposobów, ponieważ wykorzystuje wbudowany w słowniku wsparcia, bez pętli itp

var flipped = Object.keys(input).reduce(function(a,b){ 
    var tag = input[b]; 
    a[tag] = (a[tag] || '') + b; 
    return a; 
}, {}); 

Zwraca obiekt z przewracanej formacie:

// {foo: "ac", bar: "be", baz: "d"} 

Następnie wystarczy odwrócić format:

Object.keys(flipped).reduce(function(a,b){ 
    a[flipped[b]]=b; 
    return a; 
}, {}); 

Wyjście:

// {ac: "foo", be: "bar", d: "baz"} 
Powiązane problemy