Question

J'ai un objet comme:

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

Je veux réduire les doublons comme:

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

Qu'est-ce qu'une bonne façon de le faire?

Quelques mises en garde:

  • Il n'y aura jamais qu'un petit nombre de paires. (Il y a actuellement 7, je pouvais l'imaginer aller jusqu'à, disons, 20).
  • Les noms de propriétés initiales ne jamais être un seul caractère, comme dans l'exemple
  • Les valeurs pourrait atteindre plusieurs centaines de caractères.
  • à la fois la longueur du code et la vitesse sont très importants, mais étant donné le petit nombre de lignes, la clarté du code est probablement encore plus important.
Était-ce utile?

La solution

Passez par chaque propriété de l'objet et construction autre objet, où les clés sont les valeurs de la première, et les valeurs sont des listes de clés (du premier). Ensuite, vous revenez à travers ce second objet et rendre le résultat final.

Quelque chose comme ceci:

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

Maintenant, si les valeurs de l'objet d'origine ne sont pas des chaînes, alors les choses se impliquer davantage: seules les chaînes peuvent être des clés de propriété dans un objet Javascript. Vous pouvez regarder autour d'une mise en œuvre de hachage plus générale, dans ce cas. Si vos objets ont tendance à être assez petit (moins de 10 ou si les propriétés) vous pouvez écrire le n 2 version où vous parcourons simplement sur les propriétés puis itérer à nouveau pour chacun d'eux. Ce serait probablement une mauvaise idée si vos objets pourraient être importants et vous devez effectuer cette opération beaucoup.

Autres conseils

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

Utiliser comme:

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

var ob2 = Reduce(obj);

Ceci est le plus que je pouvais l'obtenir:

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

Explication:

  • Il boucle à travers chaque élément de l'objet original, obj et produit un nouvel objet, newObj.
  • Pour chaque élément dans l'original, il recherche la newObj demi-produit pour la même valeur. - Le résultat est j, que ce soit le nom de la propriété se trouve, ou une chaîne vide sinon.
  • Dans les deux cas, le nouvel objet a besoin d'une propriété du même nom que la propriété actuelle dans l'objet d'origine, plus cette valeur de j.
  • Il supprime également la propriété trouve dans newObj s'il y avait un, afin d'éviter les doublons en cours de construction.

Certes, le réglage j = "" dans la boucle est inefficace. Cela peut facilement être remplacé par un second jeu variable à "" initialement, et j que si une correspondance est trouvée. J'ai décidé d'aller pour la simplicité même.

Sans une boucle juste bibliothèque supérieure nature chaque paire (utilisation hasOwnProperty) et ajouter / append la clé d'un histogramme où la clé de l'histogramme est la valeur paire et les valeurs de l'histogramme sont les clés concaténés. Ensuite, on inverse la clé / valeurs de l'histogramme.

Modifier. Si les valeurs initiales ne sont pas des chaînes (et ne correspondent pas réversiblement) puis une bibliothèque existante « de hachage d'identité » pourrait encore permettre l'approche ci-dessus au travail

Vous pouvez mapper dire, [[k,v],...] et trier puis utiliser une approche similaire à un seau sort (imaginez il est déjà trié) aux valeurs de fusion des « clés égales » dans la passe de sortie.

Il peut se présenter comme suit (alors que le code peut avoir des bugs, l'approche est son - il travaillera également avec des objets arbitraires en tant que valeurs tant que vous avez un moyen de comparer les valeurs):

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

Pardonnez-moi si je suis complètement, mais il me semble que la façon dont vous combinez ces derniers, vous avez les clés et les valeurs dans le mauvais sens. Qu'en est-ce?

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

devrait être assez facile à convertir:

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-compilé,. Il pourrait y avoir quelques erreurs)

Commencez par une réduire qui utilise un dictionnaire pour compter les balises d'une manière basculée. Manière très performante car elle utilise le dictionnaire intégré support, pas pour les boucles etc.

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

Retourne un objet avec le format basculée:

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

Ensuite, il suffit de retourner le format suivant:

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

Sortie:

// {ac: "foo", be: "bar", d: "baz"}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top