Domanda

Ho un oggetto come:

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

Voglio ridurre i duplicati come:

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

Che cosa è un buon modo per farlo?

Qualche distinguo:

  • Non ci sarà sempre e solo un piccolo numero di coppie. (Attualmente ci sono 7; potevo immaginare di andare fino a, diciamo, 20).
  • I nomi delle proprietà iniziali sarà sempre solo un singolo carattere, come nell'esempio
  • I valori potrebbero potenzialmente funzionare a diverse centinaia di caratteri.
  • sia la velocità e il codice di lunghezza sono molto importanti, ma dato il piccolo numero di righe, codice chiarezza è probabilmente ancora più importante.
È stato utile?

Soluzione

passare attraverso ogni proprietà dell'oggetto e costrutto altro oggetto, dove le chiavi sono i valori della prima, ed i valori sono liste di chiavi (dal primo). Poi si torna indietro attraverso quella secondo oggetto e rendere il risultato finale.

Qualcosa di simile a questo:

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;
}

Ora, se i valori dell'oggetto originale non sono stringhe, allora le cose si fanno più coinvolti: solo le stringhe possono essere le chiavi di proprietà in un oggetto JavaScript. Si potrebbe guardarsi intorno per un'implementazione più generale hash, in quel caso. Se gli oggetti tendono ad essere piuttosto piccole (meno di 10 o giù di lì proprietà) si potrebbe scrivere il n 2 versione, dove è sufficiente un'iterazione sulle proprietà e poi iterare ancora per ciascuno di essi. Che probabilmente sarebbe una cattiva idea se gli oggetti potrebbero essere di grandi dimensioni e si deve eseguire questa operazione molto.

Altri suggerimenti

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;
};

Usa come:

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

var ob2 = Reduce(obj);

Questa è la più breve ho potuto farlo:

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];
}

Spiegazione:

  • itera ogni elemento nell'oggetto originale, obj, e produce un nuovo oggetto, newObj.
  • Per ogni elemento in originale, cerca la newObj mezza prodotta per lo stesso valore. - Il risultato è j, sia il nome della proprietà, se trovato, o una stringa vuota se non è.
  • In entrambi i casi, il nuovo oggetto richiede una proprietà del stesso nome della proprietà corrente nell'oggetto originale, più questo valore di j.
  • Si elimina anche la proprietà trovata in newObj se ci fosse uno, per evitare i duplicati in costruzione.

Certo, impostazione j = "" all'interno del ciclo è inefficiente. Questo può essere facilmente sostituito con un secondo insieme variabile "" inizialmente, e j solo se viene trovata una corrispondenza. Ho deciso di andare per la semplicità però.

Senza un maggiore genere solo ciclo libreria ciascuna coppia (uso hasOwnProperty) e aggiungere / append la chiave per un istogramma in cui la chiave istogramma è il valore coppia e dei valori dell'istogramma sono le chiavi concatenati. Poi invertire il tasto / valori dell'istogramma.

Modifica:. Se i valori iniziali non sono stringhe (e non reversibile mappa) poi una libreria 'identità hash' esistente potrebbe ancora consentire l'approccio di cui sopra al lavoro

In alternativa, è possibile associare a dire, [[k,v],...] e ordinamento e quindi utilizzare un approccio simile a un bucket sort (immagino che sia già ordinato) a valori di unione di "chiavi uguali" nel passaggio di uscita.

Può andare in questo modo (mentre il codice può avere bug, l'approccio è suono - funzionerà anche con oggetti arbitrari come valori fino a quando si dispone di un modo per confrontare i valori):

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

Perdonami se sono completamente fuori, ma mi sembra che il modo in cui si sta combinando questi, hai le chiavi ed i valori nel modo sbagliato. Che dire di questo?

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

Dovrebbe essere abbastanza facile da convertire:

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-compilato,. Ci potrebbe essere un paio di errori)

Inizia con un ridurre che utilizza un dizionario di contare i tag in modo capovolto. Modo molto performante in quanto utilizza il costruito nel dizionario sostegno, non per i cicli ecc.

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

Restituisce un oggetto con il formato capovolto:

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

Poi basta capovolgere il formato:

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

Output:

// {ac: "foo", be: "bar", d: "baz"}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top