2011-01-06 18 views
5

Tengo un objeto como:reducir duplicado en un objeto de javascript

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

Quiero reducir los duplicados como:

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

Qué es una buena manera de hacer eso?

algunas advertencias:

  • sólo habrá alguna vez un pequeño número de pares. (Actualmente hay 7; podría imaginar que vaya hasta, por ejemplo, 20.)
  • Los nombres de las propiedades iniciales solo serán un solo carácter, como en el ejemplo
  • Los valores podrían ejecutarse en varios cientos de caracteres .
  • Tanto la velocidad como la longitud del código son muy importantes, pero dado el pequeño número de filas, la claridad del código probablemente sea aún más importante.

Respuesta

0

pasar por cada propiedad del objeto y construir otro objeto, donde las claves son los valores de la primera, y los valores son listas de claves (de la primera). Luego vuelves a través de ese segundo objeto y haces el resultado final.

Algo como esto:

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

Ahora bien, si los valores del objeto original no son cadenas, entonces las cosas se ponen más complicado: sólo cadenas pueden ser claves de propiedad de un objeto Javascript. En ese caso, podría buscar una implementación de hash más general. Si sus objetos tienden a ser bastante pequeños (menos de 10 o más propiedades) puede escribir la versión n , donde simplemente itera sobre las propiedades y luego itera nuevamente para cada una. Sin embargo, eso probablemente sería una mala idea si sus objetos pueden ser grandes y tiene que realizar esta operación mucho.

+0

Estaba solo levantando esa idea. Pero, ¿qué sucede si uno de los valores no es una cadena? – sprugman

+0

@sprugman ah bueno eso hace las cosas un poco más involucradas ... – Pointy

+0

Sí, después de la prueba, si tiene un objeto como valor, termina con filas de resultados como {x: "[object Object]"} – sprugman

1

Sin una biblioteca de tipo superior, simplemente haga un bucle cada par (use hasOwnProperty) y agregue/añada la clave a un histograma donde la clave del histograma es el valor del par y los valores del histograma son las claves concatenadas. A continuación, invierta la clave/valores del histograma.

Editar: Si los valores iniciales no son cadenas (y no se pueden mapear de forma reversible), entonces una biblioteca existente 'hash de identidad' podría permitir que el enfoque anterior funcione.

Como alternativa, puede asignar decir, [[k,v],...] y clasificar y luego utilizar un enfoque similar al de un bucket sort (imagina que ya está ordenadas) para combinar los valores de "claves iguales" en el pase de salida.

Puede ir como esto (mientras que el código puede tener errores, el enfoque es el sonido - que también trabajará con objetos arbitrarios como los valores, siempre y cuando usted tiene una manera de comparar los valores):

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

Creo que podría necesitar un ejemplo de código, a menos que signifique lo mismo que Pointy. – sprugman

+0

@sprugman 100% no probado código-muestra publicado ;-) –

+0

Decidí que siempre serían cuerdas, así que puedo usar la versión más simple, pero gracias por esto. Por cierto, en la línea 4, creo que te refieres a '_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; 
}; 

Utilizar como:

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

var ob2 = Reduce(obj); 
+0

Este es esencialmente el mismo que el de Pointy. Me gusta el tuyo por su claridad, pero usa useOwnProperty y fue el primero, así que le di el cheque. Gracias, sin embargo. – sprugman

1

Esta es la más corta que podía conseguirlo:

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

Explicación:

  • Se recorre cada elemento en el objeto original, obj, y produce un nuevo objeto, newObj.
  • Para cada elemento en el original, busca el newObj medio producido para obtener el mismo valor. - El resultado es j, ya sea el nombre de la propiedad si se encuentra, o una cadena vacía si no.
  • En cualquier caso, el nuevo objeto necesita una propiedad con el mismo nombre que la propiedad actual en el objeto original, más este valor de j.
  • También elimina la propiedad encontrada en newObj si la hay, para evitar que se creen duplicados.

Es cierto que la configuración j = "" dentro del ciclo es ineficaz. Esto se puede reemplazar fácilmente con una segunda variable establecida en "" inicialmente, y j solo si se encuentra una coincidencia. Sin embargo, decidí ir por la simplicidad.

+0

Decidí que siempre serían cadenas, así que puedo usar la solución O (n) más simple. Gracias por esto, sin embargo. – sprugman

0

Perdóneme si estoy completamente fuera, pero me parece que la forma en que las combina, tiene las claves y los valores equivocados. ¿Qué hay de esto?

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

debe ser lo suficientemente fácil de 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); 
    } 
} 

(. Cerebro-compilado; puede haber un par de errores)

+0

Tengo lo que tengo que comenzar y quiero lo que quiero terminar. :) – sprugman

+0

sprugman: Es suficiente. No tenemos mucho contexto, así que pensé que ofrecería una solución alternativa. –

0

de inicio con un reducen que utiliza un diccionario para contar el etiquetas de una manera volteada. camino de buen calidad, ya que utiliza el construido en el diccionario de apoyo, sin los bucles etc.

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

Devuelve un objeto con formato volteado:

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

A continuación, sólo darle la vuelta al formato:

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

Salida:

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