Eu tenho dois objetos: oldObj
e newObj
.
Os dados oldObj
foram usados para preencher um formulário e newObj
são o resultado do usuário alterando os dados neste formulário e enviando-os.
Ambos os objetos são profundos, ie. eles têm propriedades que são objetos ou matrizes de objetos etc. - podem ter n níveis de profundidade, portanto o algoritmo diff precisa ser recursivo.
Agora eu preciso não apenas descobrir o que foi alterado (como adicionado / atualizado / excluído) de oldObj
para newObj
, mas também a melhor forma de representá-lo.
Até agora, meus pensamentos eram apenas construir um genericDeepDiffBetweenObjects
método que retornasse um objeto no formulário, {add:{...},upd:{...},del:{...}}
mas então pensei: alguém já deveria ter precisado disso antes.
Então ... alguém sabe de uma biblioteca ou de um código que fará isso e talvez tenha uma maneira ainda melhor de representar a diferença (de uma maneira que ainda seja serializável em JSON)?
Atualizar:
Eu pensei em uma maneira melhor de representar os dados atualizados, usando a mesma estrutura de objeto que newObj
, mas transformando todos os valores de propriedade em objetos no formulário:
{type: '<update|create|delete>', data: <propertyValue>}
Então, se newObj.prop1 = 'new value'
e oldObj.prop1 = 'old value'
definiriareturnObj.prop1 = {type: 'update', data: 'new value'}
Atualização 2:
Fica realmente complicado quando chegamos a propriedades que são matrizes, já que a matriz [1,2,3]
deve ser contada como igual a [2,3,1]
, o que é simples o suficiente para matrizes de tipos baseados em valor, como string, int e bool, mas fica realmente difícil de lidar quando se trata de matrizes de tipos de referência, como objetos e matrizes.
Matrizes de exemplo que devem ser encontradas iguais:
[1,[{c: 1},2,3],{a:'hey'}] and [{a:'hey'},1,[3,{c: 1},2]]
Não é apenas complexo verificar esse tipo de profunda igualdade de valores, mas também descobrir uma boa maneira de representar as mudanças que possam ser.
fonte
newObj
é gerado pelos valores de leitura de código js de um formulário no DOM. Existem várias maneiras de manter o estado e fazer isso com muito mais facilidade, mas eu gostaria de mantê-lo apátrida como exercício. Também estou procurando arte anterior para ver como os outros podem ter enfrentado isso, se é que alguém já o fez.Respostas:
Eu escrevi uma turma que está fazendo o que você quer, você pode testá-lo aqui .
A única coisa diferente da sua proposta é que eu não considero
[1,[{c: 1},2,3],{a:'hey'}] and [{a:'hey'},1,[3,{c: 1},2]]
a mesma coisa, porque acho que as matrizes não são iguais se a ordem de seus elementos não for a mesma. Claro que isso pode ser alterado, se necessário. Além disso, esse código pode ser aprimorado ainda mais para que funcione como argumento que será usado para formatar objetos diff de maneira arbitrária, com base nos valores primitivos passados (agora esse trabalho é feito pelo método "compareValues").fonte
c
é criado comoundefined
deve ser a string'i am created'
) e, além disso, ele não faz o que eu preciso, pois falta o valor profundo da matriz compare, que é o parte mais crucial (e complexa / difícil). Como observação lateral, a construção'array' != typeof(obj)
é inútil, pois matrizes são objetos que são instâncias de matrizes.{type: ..., data:..}
objeto. O que está faltando é pesquisar valor da primeira matriz em segundo, mas, como mencionei na minha resposta, não acho que as matrizes sejam iguais se a ordem de seus valores não for igual ([1, 2, 3] is not equal to [3, 2, 1]
na minha opinião).[{key: 'value1'}] and [{key: 'value2'}, {key: 'value3'}]
. Agora é o primeiro objeto na primeira matriz atualizado com "valor1" ou "valor2". E este é um exemplo simples, pode ficar muito complicado com o aninhamento profundo. Se você quiser / precisar comparação profunda do assentamento, independentemente da posição da chave não criar matrizes de objetos, criar objetos com objetos aninhados como por exemplo anterior:{inner: {key: 'value1'}} and {inner: {key: 'value2'}, otherInner: {key: 'value3'}}
.Usando Underscore, uma simples comparação:
Resultados nas partes
o1
correspondentes, mas com valores diferentes emo2
:Seria diferente para uma diferença profunda:
Conforme apontado por @Juhana nos comentários, o acima é apenas um diff a -> be não é reversível (o que significa que propriedades extras em b seriam ignoradas). Use em vez disso a -> b -> a:
Veja http://jsfiddle.net/drzaus/9g5qoxwj/ para obter exemplos completos + testes + mixins
fonte
omit
que seria uma dificuldade profunda, mas estava errado, então incluímos também para comparação.r[k] = ... : v
emr[k] = ... : {'a':v, 'b':b[k] }
, desta forma você pode ver dois valores.{a:1, b:2}
e{a:1, b:2, c:3}
._.omitBy
vez de_.omit
.Eu gostaria de oferecer uma solução ES6 ... Essa é uma diferença unidirecional, o que significa que ele retornará chaves / valores
o2
que não são idênticos aos seus equivalentes emo1
:fonte
if(o1[key] === o1[key])
linha de caraUncaught SyntaxError: Unexpected token ...
Usando o Lodash:
Eu não uso chave / objeto / fonte, mas deixei lá se você precisar acessá-los. A comparação de objetos apenas impede que o console imprima as diferenças no console do elemento mais externo para o elemento mais interno.
Você pode adicionar alguma lógica interna para manipular matrizes. Talvez classifique as matrizes primeiro. Esta é uma solução muito flexível.
EDITAR
Alterado de _.merge para _.mergeWith devido à atualização do lodash. Obrigado Aviron por perceber a mudança.
fonte
Aqui está uma biblioteca JavaScript que você pode usar para encontrar diferenças entre dois objetos JavaScript:
URL do Github: https://github.com/cosmicanant/recursive-diff
URL do Npmjs: https://www.npmjs.com/package/recursive-diff
Você pode usar a biblioteca diff-recursiva no navegador e também no Node.js. Para navegador, faça o seguinte:
Enquanto no node.js você pode exigir o módulo 'recursive-diff' e usá-lo como abaixo:
fonte
Atualmente, existem alguns módulos disponíveis para isso. Recentemente, escrevi um módulo para fazer isso, porque não estava satisfeito com os vários módulos diferentes que encontrei. É chamado
odiff
: https://github.com/Tixit/odiff . Também listei vários módulos mais populares e por que eles não eram aceitáveis no leia-meodiff
, dos quais você pode dar uma olhada seodiff
não tiver as propriedades que deseja. Aqui está um exemplo:fonte
Existe um módulo npm com mais de 500 mil downloads semanais: https://www.npmjs.com/package/deep-object-diff
Gosto do objeto como representação das diferenças - especialmente é fácil ver a estrutura quando está formatada.
fonte
Eu usei esse pedaço de código para executar a tarefa que você descreve:
isso fornecerá um novo objeto que mesclará todas as alterações entre o objeto antigo e o novo objeto do seu formulário
fonte
$.extend(true,obj1,obj2)
usar o jQuery. Não é disso que eu preciso. Preciso da diferença entre os dois objetos, não da combinação deles.Eu desenvolvi a função denominada "compareValue ()" em Javascript. retorna se o valor é igual ou não. Eu chamei compareValue () no loop for de um Object. você pode obter a diferença de dois objetos no diffParams.
fonte
Sei que estou atrasado para a festa, mas precisava de algo semelhante que as respostas acima não ajudassem.
Eu estava usando a função $ watch do Angular para detectar alterações em uma variável. Não só precisava saber se uma propriedade havia mudado na variável, mas também queria ter certeza de que a propriedade que mudou não fosse um campo calculado temporário. Em outras palavras, eu queria ignorar certas propriedades.
Aqui está o código: https://jsfiddle.net/rv01x6jo/
Veja como usá-lo:
Espero que isso ajude alguém.
fonte
Eu apenas uso ramda, para resolver o mesmo problema, eu preciso saber o que é alterado no novo objeto. Então aqui está o meu design.
O resultado é, nome da propriedade e seu status.
fonte
Aqui está uma versão datilografada do código @sbgoran
fonte
Aqui está uma versão modificada de algo encontrado no gisthub .
fonte
Modifiquei a resposta de @ sbgoran para que o objeto diff resultante inclua apenas os valores alterados e omita os valores iguais. Além disso, mostra o valor original e o valor atualizado .
fonte
Eu já escrevi uma função para um dos meus projetos que comparará um objeto como opções de usuário com seu clone interno. Ele também pode validar e até substituir por valores padrão se o usuário digitar dados incorretos ou removidos, em javascript puro.
No IE8, 100% funciona. Testado com sucesso.
/ * resultado
fonte
A função mais extensa e simplificada da resposta de sbgoran.
Isso permite uma varredura profunda e encontra a similaridade de uma matriz.
fonte
Tropecei aqui, tentando encontrar uma maneira de obter a diferença entre dois objetos. Esta é a minha solução usando o Lodash:
fonte
Peguei a resposta acima por @sbgoran e a modifiquei para o meu caso da mesma forma que a pergunta necessária, para tratar matrizes como conjuntos (ou seja, a ordem não é importante para diff)
fonte
Aqui está uma solução que é:
object
tipo)undefined
Primeiro, definimos a interface do resultado da comparação:
com o caso especial de mudança, onde queremos saber quais são os valores antigos e novos:
Então, podemos fornecer a
diff
função que é apenas dois loops (com recursividade sedeep
fortrue
):Como exemplo, chamando:
retornará:
e chamar o mesmo com o
deep
terceiro parâmetro retornará:fonte
Essa biblioteca é uma das mais rápidas a fazer diferenças de objetos profundos:
https://www.npmjs.com/package/@netilon/differify
fonte