How to find different property of two objects.

I have two objects they all the same type. I want to compare the two objects and extract which properties are different. Besides using a loop to iterate, is there a better way?

  Discussion posts and replies are publicly visible

  • +1
    Certified Lead Developer

    Without Loop is not possible.
    No built-in way to dynamically index multiple properties without iteration.

    a!localVariables(
      local!employee1: {
        name: "John",
        age: 30,
        dept: "IT",
        salary: 50000
      },
      local!employee2: {
        name: "John",
        age: 35,
        dept: "IT", 
        salary: 55000
      },
    
      /* Get all property names */
      local!allKeys: a!keys(local!employee1),
    
      /* Check which properties are different */
      local!areEqual: a!forEach(
        local!allKeys,
        index(local!employee1, fv!item) = index(local!employee2, fv!item)
      ),
    
      /* Find properties with different values */
      local!differentProps: index(
        local!allKeys,
        wherecontains(false, local!areEqual),
        null
      ),
    
      /* Show results */
      {
        differentProperties: local!differentProps,
    
        oldValues: a!forEach(
          local!differentProps,
          index(local!employee1, fv!item, null)
        ),
        /* Output: {30, 50000} */
    
        newValues: a!forEach(
          local!differentProps,
          index(local!employee2, fv!item, null)
        )
        /* Output: {35, 55000} */
      }
    )

  • 0
    Certified Lead Developer

    If you want just to check whether the variables/datasets are different or not then convert the datasets into json using a!toJson() and then compare like

     

    a!tojson(a!map(key: 2, value: 2)) = a!tojson(a!map(key: 1, value: 2))

    If you want a list of attributes and values which vary between two datasets then a!foreach() is needed atleast once to parse and check each field in the datatype.

    Below is a solution with two expressions which can help with this part. In one expression we just have simple expression to compare 2 values which returns true if values dont match.

    In main expression we have code like below which filters and returns the field and values where match failed. 

    a!localVariables(
      local!map1: { name: "Alex", age: "22", dob: today() },
      local!map2: { name: "Alexa", age: "22", dob: today() - 4 },
      local!keys: a!keys(local!map1),
      a!foreach(
        local!keys,
        a!map(
          field: fv!item,
          value: filter(
            rule!HSS_CompareValues,
            merge(
              { index(local!map1, fv!item, {}) },
              { index(local!map2, fv!item, {}) },
              
            )
          )
        )
      )
    )

    As this uses just one foreach() which iterates for each field in a datatype it will be run for fixed times always and wont affect the performance adversely. Hope this helps!

  • 0
    Certified Lead Developer
    in reply to Harsha Sharma

    This one is interesting in how it avoids issues with invalid type comparisons by casting everything to text via the filter predicate rule. I'll have to keep it in mind for the future as it makes things much more resilient in most use cases. What's fun - I think there's a way to change the a!forEach() into a fn!reduce, to remove the "loop", but in reality that just masks the loop within a looping function.

    The only caution with this approach for a generic rule is when dealing with numbers that have leading or trailing zeros - for example, 0.20 should be considered equal to 0.2 in many finance contexts.

  • Ok I got it, but what if my data structure is a nested structure? like this. Can I call this method recursively like in Java?

     local!employee1: {
        name: "John",
        age: 30,
        dept: {
            name: "IT",
            field2: xx,
            field3: xx
        },
        salary: 50000
      }

  • 0
    Certified Lead Developer
    in reply to Peon

    Doesn't support recursive function calls(you can simulate it by having an expression rule call itself conditionally to compare nested maps, checking property types and combining differences from each level)
    For nested structures:
    Compare each level separately (manual nesting)
    Flatten structure first, then compare
    JSON comparison -> a!toJson(obj1) = a!toJson(obj2) (only tells If different, not what)

    Best approach as per my understanding, Handle each nesting level explicitly with separate comparison logic.

  • Thanks, I want to create a common utils like this. However, it seems a bit difficult at the moment. Besides the problem you mentioned, I also find it hard to determine whether an object is a nested object.