There's an object where its data matches with another object, yet they have different structures.
For example, consider this scenario:
The 'Team' object holds the team ID as its key. The 'Team' object contains 'name' and 'users' objects as its values. The 'users' object has the user ID as its key, which doesn't overlap with user IDs from other teams.
So, I want to create a new object that has all users.
The 'users' object can be subscribed to by users, and modifying this should reflect changes in the 'Team' object. Conversely, the 'Team' object can be subscribed to by users, and modifying it should reflect changes in the 'users' object.
How can I achieve this?
I attempted to update each object using a 'subscribe' function in JavaScript files, but I ended up stuck in an infinite loop and failed.
Here's an example code and a REPL."
<script>
import {writable} from "svelte/store";
const teamDict = writable({})
const userDict = writable({})
function initTeamDict() {
teamDict.set({
1: {
name: "good team",
users: {
1: "James",
2: "Poppy",
48: "Hway"
}
},
2: {
name: "bad team",
users: {
47: "Kelin",
35: "Teo",
24: "Ferma"
}
}
})
}
function initUserDict() {
userDict.set(Object.values($teamDict).reduce((acc, team) => ({...acc, ...team[`users`]}), {}))
}
</script>
<button on:click={initTeamDict}>init team dict</button>
<button on:click={initUserDict}>init user dict</button>
<div> {JSON.stringify($teamDict)}</div>
<div> {JSON.stringify($userDict)}</div>
<button on:click={() => $teamDict[`1`][`users`][`1`] = "top"}>this button should change userDict also </button>
<button on:click={() => $userDict[`1`] = "bottom"}>this button should change teamDict also </button>
REPL
https://svelte.dev/repl/e6570ac9ca464c15967a43c8311dcd4d?version=4.2.8
Edit
With the help of @ghostmodd's answer, I solved it by writing the following code.
I copied the object and modified it because modifying a copied object does not trigger a subscription.
The modified object may not be rendered in the subscription order, so I created a separate view using derived.
<script>
import {derived, writable} from "svelte/store";
import {Button} from "flowbite-svelte";
const teamDict = writable({})
const userDict = writable({})
teamDict.subscribe(($$teamDict) => {
const userDictCopy = $userDict
for (const key in userDictCopy) {
delete userDictCopy[key]
}
Object.assign(userDictCopy, Object.values($$teamDict).reduce((acc, team) => ({...acc, ...team[`users`]}), {}))
})
userDict.subscribe(($$userDict) => {
const teamDictCopy = $teamDict
for (const team of Object.values(teamDictCopy)) {
team[`users`] = {}
}
for (const [userId, user] of Object.entries($$userDict)) {
teamDictCopy[user[`team_id`]][`users`][userId] = user
}
})
const storeView = derived(
[teamDict, userDict],
([$teamDict, $userDict], set) => {
set({teamDict: $teamDict, userDict: $userDict})
}
)
function initTeamDict() {
teamDict.set({
1: {
name: "good team",
users: {
1: {
"name": "James",
"team_id": 1
},
2: {
"name": "Poppy",
"team_id": 1
},
48: {
"name": "Hway",
"team_id": 1
}
}
},
2: {
name: "bad team",
users: {
47: {
"name": "Kelin",
"team_id": 2
},
35: {
"name": "Teo",
"team_id": 2
},
24: {
"name": "Ferma",
"team_id": 2
}
}
}
})
}
</script>
<Button on:click={initTeamDict}>init team dict</Button>
<div> {JSON.stringify($storeView.teamDict)}</div>
<div> {JSON.stringify($storeView.userDict)}</div>
<Button on:click={() => $teamDict[`1`][`users`][`1`][`name`] = "top"}>this button should change userDict also </Button>
<Button on:click={() => $userDict[`1`][`name`] = "bottom"}>this button should change teamDict also </Button>
REPL https://svelte.dev/repl/0b89313017234b8d93d94a4a6adf38ba?version=4.2.8
Actually, you can achieve that using intermediate derived store. The derived store is similar to React`s useEffect hook. It watches for updates to specified stores and handles them.
So, except for the "Teams" and "Users" stores you should create a derived store which will catch and save updates.
That`s how I did it. Firstly, I created original writable stores with an empty object value.
Then, I made the intermediate derived store which will follow previously created entities.
Pay attention to the arguments. The first argument is a list of observable stores. The second argument is a function which handles updates. The third argument is an initial value of the derived store.
The third step is to save the required data in the derived store. So, I wrote a handler function in this purpose.
Link to REPL: https://svelte.dev/repl/1966610b5c6c47e497918911ac1d8269?version=4.2.8
P.S. sorry for my English. I'm still learning it :3