iRon7 / Join-Object

Combines two objects lists based on a related property between them.
MIT License
107 stars 13 forks source link

Missing recursion for PSCustomObjects within Property Values #25

Closed ScrambledBrain closed 3 years ago

ScrambledBrain commented 3 years ago

Hi, your Join-Object actually joins values only at 1st level as wanted by parameters given.

If a property does not consist of a flat value (like string or int and so on) but consists of another PSCustomObject with multiple Properties and values, your script overwrites the whole PSCustomObject-value with the other PSCustomObject-value instead of comparing them, which sub-value needs an update or which sub-property needs to be added regarding to the parameters given.

Maybe have a look at https://gist.github.com/Badabum/a61e49019fb96bef4d5d9712e07b2af7 who includes the recursion.

Hopefully you can add recursion too =)

Cheers.

iRon7 commented 3 years ago

@ScrambledBrain,

Thanks fot the idea but I am afraid that it will add to much complexity to the design and usage. Taken to objects like:

$a = ConvertFrom-jSON '
{
    "One": 1,
    "Two": 2,
    "Three": {
        "One": 11,
        "Two": 22,
        "Three": 33
    }
}'

$b = ConvertFrom-jSON '
{
    "One": 1,
    "Two": 22,
    "Three": {
        "One": 11,
        "Two": 2,
        "Three": 33
    }
}'

What would be the results of:

$a |Join $b -On One
$a |Join $b -On Two
$a |Join $b -On Three

And for a full join? should a parent with a child match considered as a whole or each (recursive) child as a separate relation?

$a |FullJoin $b -On One
$a |FullJoin $b -On Two
$a |FullJoin $b -On Three

I think it is better to use a separate function as Flatten-Object for this.

ScrambledBrain commented 3 years ago

@iRon7,

I need the function to update my imported JSON file with settings I need. It has to update existing values, add new properties with its values and let other properties stay as before. But this has to work with child properties too, not only on 1st level, so, flattening is not an option.

Unfortunately, PowerShell seems not to have functionality included for handling this need for JSON files directly and also not for PSObjects directly.

I hoped, your Join-Object covers it, but did not, so I just dropped an info here.

The function(s) at link mentioned above fulfilled this for me and truely go through the whole PSObject recursively.

iRon7 commented 3 years ago

@ScrambledBrain,

I have given it some more thought but I think that what you're suggesting is not possible from a common design view. The general idea behind the Join-Object cmdlet is to join/merge tables where a table is a list of objects (or hash tables, e.g. when using -AsHashTable) each property reprecents a column. The relation between the list of objects is controlled by the -On parameter (and if omitted, based on the line index). How the properties are merged is defined by the -Properties parameter. The problem is that there generally exist two recursive structures: Arrays and dictionaries (or objects) meaning that from a recursive view you might have arrays in arrays, dictionaries in arrays, arrays in dictionaries and dictionaries in dictionaries. I suspect that you only covering dictionaries in dictionaries in your prototype. Even with a suggested -Recurse parameter that is mutual exclusive with on it would be to my opinion impossible to control the in-depth (array) values and objects with another recursive list. Taken this Wikipedia example for 1.json:

{
  "firstName": "John",
  "lastName": "Smith",
  "isAlive": true,
  "age": 27,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postalCode": "10021-3100"
  },
  "phoneNumbers": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    }
  ],
  "children": [],
  "spouse": null
}

What will the 2.json structure be to:

ScrambledBrain commented 3 years ago

@iRon7

yes, if I would update that data with ...

{
  "firstName": "John",
  "lastName": "Smith",
  "phoneNumbers": [
    {
      "type": "home",
      "number": "212 555-9999"
    },
    {
      "type": "mobile",
      "number": "999 555-8888"
    }
  ]
}

... the home phone number should be updated, the mobile phone number should be added and the office phone number should stay as it is.

iRon7 commented 3 years ago

if this:

    {
      "type": "home",
      "number": "212 555-9999"
    },

Replaces the

    {
      "type": "home",
      "number": "212 555-1234"
    },

Why wouldn't

    {
      "type": "mobile",
      "number": "999 555-8888"
    }

replace the whole entry:

    {
      "type": "office",
      "number": "646 555-4567"
    }

???

There is no difference between the two entries. (because both Type and number are different?, if yes, what should the 2.Json be if I do want to replace both?) In other words, what defines what should be added and what defines what should be replaced? (not even talking about: how should I remove an entry?)

If you suggesting that e.g. an -On type parameter should take care of this, then how should the cmdlet differentiate between a possible parent type:[... at a higher level or a different branch?

ScrambledBrain commented 3 years ago

Ah yep, JSON wouldn't "know" that. It is a pair validation of "property" : "value".

But these pairs are separated in 2 blocks within phoneNumbers. Each block should be handled by its own... maybe sorted before. But yep, that would be not easy to find out, which phone block should match to another.

But that case was not my case, so I didn't think about that in depth.

I used the other PSScript linked above for a case like this:

original:

{
  "p1": "v1",
  "p2": "v2",
  "propertygroup1": {
      "p1": "v1",
      "p2": "v2"
  }
}

update:

{
  "p1": "v1111",
  "p3": "v3",
  "propertygroup1":{
      "p1": "v1111",
      "p3": "v3"
  },
  "propertygroup2":{
      "p1": "v1"
  }
}

result:

{
  "p1": "v1111",
  "p2": "v2",
  "p3": "v3",
  "propertygroup1":{
      "p1": "v1111",
      "p2": "v2",
      "p3": "v3"
  },
  "propertygroup2":{
      "p1": "v1"
  }
}
iRon7 commented 3 years ago

But that case was not my case, so I didn't think about that in depth.

Exactly, which just makes it too specific to embed in a common Join-Object script. Meaning, I can't just add a recursion feature that only works for 50% (dictionaries but not arrays). Anyone who will use this purposed feature might bounce into this limitation and report a bug which I can't resolve.

Unless you have some better idea how to implement this as a whole, I will close this issue.

iRon7 commented 3 years ago

I have closed this purpose as I do not have clear design for a full implementation for both recursive dictionaries and recursive arrays.