How do I normalize/denormalize a tree? - javascript

I have a json tree structure that I want to normalize into something like a hashmap and then denormalize it back to a tree if needed.
I have a very dynamic tree that I want to use as state in my react-redux project, but for that I somehow need to transform the data so that I can access it without having to search elements recursively in the tree each time I want to update/access the state.
const actual = {
id: "1",
type: 'Container',
data: {},
items: [
{
id: "2",
type: "Subcontainer",
data: {
title: "A custom title",
text: "A random Headline"
},
items: []
},
{
id: "3",
type: "Subcontainer",
data: {
title: "A custom title",
text: "A random Headline"
},
items: []
}
]
};
Now I want to transform it into something like:
const expected = {
1: {
id: "1",
type: 'Container',
data: {},
items: [1, 2]
},
2: {
id: "2",
type: "Subcontainer",
data: {
title: "A custom title",
text: "A random Headline"
},
items: []
},
3: {
id: "3",
type: "Subcontainer",
data: {
title: "A custom title",
text: "A random Headline"
},
items: []
}
};
I found a JS lib called Normalizr, but I absolutely don't get how to create the schemas for it to work.
That was my last try, and it returns only the inner two items and also directly the data object inside without id, items around:
const data = new schema.Entity("data");
const item = new schema.Object({ data });
item.define({ items: new schema.Array(item) });
const items = new schema.Array(item);
const normalizedData = normalize(mock, items);

I'm not going to worry too much about the types, since you can alter those to meet your needs. Going off you're example, I will define
interface Tree {
id: string;
type: string;
data: {
title?: string;
text?: string;
items: Tree[];
}
}
interface NormalizedTree {
[k: string]: {
id: string;
type: string;
data: {
title?: string;
text?: string;
items: string[]
}
}
}
and we want to implement function normalize(tree: Tree): NormalizedTree and function denormalize(norm: NormalizedTree): Tree.
The normalize() function is fairly straightforward since you can recursively walk the tree and collect the normalized trees into one big normalized tree:
function normalize(tree: Tree): NormalizedTree {
return Object.assign({
[tree.id]: {
...tree,
data: {
...tree.data,
items: tree.data.items.map(v => v.id)
}
},
}, ...tree.data.items.map(normalize));
}
In English, we are making a single normalized tree with a property with key tree.id and a value that's the same as tree except the data.items property is mapped to just the ids. And then we are mapping each element of data.items with normalize to get a list of normalized trees that we spread into that normalized tree via the Object.assign() method. Let's make sure it works:
const normalizedMock = normalize(mock);
console.log(normalizedMock);
/* {
"1": {
"id": "1",
"type": "Container",
"data": {
"items": [
"2",
"3"
]
}
},
"2": {
"id": "2",
"type": "Subcontainer",
"data": {
"title": "A custom title",
"text": "A random Headline",
"items": []
}
},
"3": {
"id": "3",
"type": "Subcontainer",
"data": {
"title": "A custom title",
"text": "A random Headline",
"items": []
}
}
} */
Looks good.
The denormalize() function is a little trickier, because we need to trust that the normalized tree is valid and actually represents a tree with a single root and no cycles. And we need to find and return that root. Here's one approach:
function denormalize(norm: NormalizedTree): Tree {
// make Trees with no children
const treeHash: Record<string, Tree> =
Object.fromEntries(Object.entries(norm).
map(([k, v]) => [k, { ...v, data: { ...v.data, items: [] } }])
);
// keep track of trees with no parents
const parentlessTrees =
Object.fromEntries(Object.entries(norm).map(([k, v]) => [k, true]));
Object.values(norm).forEach(v => {
// hook up children
treeHash[v.id].data.items = v.data.items.map(k => treeHash[k]);
// trees that are children do have parents, remove from parentlessTrees
v.data.items.forEach(k => delete parentlessTrees[k]);
})
const parentlessTreeIds = Object.keys(parentlessTrees);
if (parentlessTreeIds.length !== 1)
throw new Error("uh oh, there are " +
parentlessTreeIds.length +
" parentless trees, but there should be exactly 1");
return treeHash[parentlessTreeIds[0]];
}
In English... first we copy the normalized tree into a new treeHash object where all the data.items are empty. This will eventually hold our denormalized trees, but right now there are no children.
Then, in order to help us find the root, we make a set of all the ids of the trees, from which we will remove any ids corresponding to trees with parents. When we're all done, there should hopefully be a single id left, that of the root.
Then we start populating the children of treeHash's properties, by mapping the corresponding data.items array from the normalized tree to an array of properties of treeHash. And we remove all of these child ids from parentlessTreeIds.
Finally, we should have exactly one property in parentlessTreeIds. If not, we have some kind of forest, or cycle, and we throw an error. But assuming we do have a single parentless tree, we return it.
Let's test it out:
const reconsitutedMock = denormalize(normalizedMock);
console.log(reconsitutedMock);
/* {
"id": "1",
"type": "Container",
"data": {
"items": [
{
"id": "2",
"type": "Subcontainer",
"data": {
"title": "A custom title",
"text": "A random Headline",
"items": []
}
},
{
"id": "3",
"type": "Subcontainer",
"data": {
"title": "A custom title",
"text": "A random Headline",
"items": []
}
}
]
}
} */
Also looks good.
Playground link to code

I would recommend .flatMap for this kind of transformations:
const flattenTree = element => [
element,
...element.data.items.flatMap(normalizeTree)
]
This move you from this shape:
{
id: 1,
data: { items: [
{
id: 2,
data: { items: [
{ id: 3, data: { items: [] } },
] }
] }
}
to this one:
[
{ id: 1, data: {...}},
{ id: 2, data: {...}},
{ id: 3, data: {...}},
]
Then once you have a flat array, you can transform it further to remove the references and create an object from entries:
const normalizedTree = element => {
let flat = flattenTree(element)
// only keep the id of each items:
// [{ id: 1, data:{...}}] -> [1]
// for (const el of flat) {
// el.data.items = el.data.items.map(child => child.id)
// }
// note that the for loop will change the initial data
// to preserve it you can achieve the same result with
// a map that will copy every elements:
const noRefs = flat.map(el => ({
...el,
data: {
...el.data,
items: el.data.items.map(child => child.id),
},
}))
// then if you need an object, 2 steps, get entries, [id, elem]:
const entries = noRefs.map(({ id, ...element }) => [id, element])
// then the built-in `Object.fromEntries` do all the work for you
// using the first part of the entry as key and last as value:
return Object.fromEntries(entries)
// if you have multiple objects with the same id's, only the last one
// will be in your returned object
}

Related

How to traverse through a tree like nested data structure of unknown depth in order to find and collect addressable array items?

Say I have an array that looks as such:
[{
"name": "Audiograms",
"folders": [{
"name": "2022"
}, {
"name": "2021"
}, {
"name": "2020"
}]
}, {
"name": "Patient Paperwork"
}, {
"name": "Repairs"
}]
And this array can have an infinite amount of objects and sub-objects, similar to a file tree.
I have an array letting me know the name of the folders I need to access from the root of the object, like:
["Audiograms", "2022"]
I also do not know this value ahead of time, nor do I know how many items are in this array ahead of time.
How would I be able to actually traverse this file tree using the array of names? I wish to do things like maybe pop the matching object out and move it to another part of the file tree.
Thank you!
OP
"I wish to do things like maybe pop the matching object out and move it to another part of the file tree."
In order to achieve follow-up tasks like the above mentioned one, the next provided solution walks the OP's folder structure and collects for each addressable match an object of two references, target and parent, where the former is the reference of the to be found folder-item, and the latter is the reference of its parent folder-item.
The solution got achieved by a recursively implemented reducer function.
function collectAddressableFolderRecursively(collector, folderItem) {
const { name = null, folders = [] } = folderItem;
const {
address: [parentName, childName], result,
} = collector;
if (name === parentName && folders.length) {
const targetFolder = folders
.find(({ name }) => name === childName) ?? null;
if (targetFolder !== null) {
result.push({
target: targetFolder,
parent: folderItem,
});
}
}
result.push(
...folders.reduce(collectAddressableFolderRecursively, {
address: [parentName, childName],
result: [],
})
.result
);
return collector;
}
const folders = [{
name: 'Audiograms',
folders: [{
name: '2022',
folders: [{
name: 'Audiograms',
folders: [{
name: '2022',
}, {
name: 'foo',
}],
}],
}, {
name: '2021',
}, {
name: '2020',
}]
}, {
name: 'Patient Paperwork',
}, {
name: 'Repairs',
folders: [{
name: 'Audiograms',
folders: [{
name: '2022',
}, {
name: 'bar',
}],
}, {
name: 'baz',
}],
}]
const address = ['Audiograms', '2022'];
const { result } = folders
.reduce(collectAddressableFolderRecursively, {
address,
result: [],
});
console.log({ address, result });
.as-console-wrapper { min-height: 100%!important; top: 0; }

Returning the ids from the object within the arrays

I have a multidimensional javascript array of objects that I am trying to use to simply collate the Unit id into a new array as shown below.
What is the best solution for returning the id within the inner value so I just get an array of the ids whatever I try seems to not work
[
{
units: [
{
id: 10000282,
name: "Group 1",
},
{
id: 10000340,
name: "Group 2",
},
{
id: 10000341,
name: "Group 3",
},
],
},
{
units: [
{
id: 10000334,
name: "Group 4",
},
],
},
]
Expected output - just return an array with simply the ids
e.g
ids = [ 10000282, 10000340, 10000341, 10000334 ]
Assuming that data is in variable data:
> data.map(o => o.units.map(u => u.id)).flat()
[ 10000282, 10000340, 10000341, 10000334 ]
This assumes you're in an environment where .flat() is a thing.
If that's not the case, the longer way around is
const ids = [];
data.forEach(o => {
o.units.forEach(u => {
ids.push(u.id);
});
});

Creating new data object set from multiple nested arrays

I've have a complex data structure with multiple nested arrays in place.
Below is the current structure
var contentData = {
data: {
content: [
{
type: "column",
sections: [
{
sub: [
{
type: "heading-1",
text: "Heading Text"
}
]
}
]
},
{
type: "acc-item",
sections: [
{
sub: [
{
type: "heading-1",
text: "Heading Text"
},
{
type: "ordered-item",
text: "Item 1"
},
{
type: "unordered-item",
text: "Item 2"
}
]
}
]
},
{
type: "acc-item",
sections: [
{
sub: [
{
type: "heading-1",
text: "Heading Text 2"
}
]
}
]
}
]
}
}
So What I wanted is,
I wanted to group all the ordered-item & unordered-item into a new object like {type: 'list', items:[all list items]}.
I need to extract all items which are inside sub and push it to new object embedded and it should placed in the root level like below,
{type:"acc-item",embedded:[{type:"heading-1",text:"Heading Text 2"}]};
So What I've done so far,
I can able to group acc-item, but not the ordered-item & unordered-item.
So my final expected result should like this,
[{
"type": "column",
"embedded": [
{
"type": "heading-1",
"text": "Heading Text"
}
]
},
{
"type": "acc-group",
"items": [
{
"type": "acc-item",
"embedded": [
{
"type": "heading-1",
"text": "Heading Text"
},
{
"type": "list",
"items": [
{
"type": "ordered-item",
"text": "Item 1"
},
{
"type": "unordered-item",
"text": "Item 2"
}
]
}
]
},
{
"type": "acc-item",
"embedded": [
{
"type": "heading-1",
"text": "Heading Text 2"
}
]
}
]
}]
Below is my code,
var group,contentData={data:{content:[{type:"column",sections:[{sub:[{type:"heading-1",text:"Heading Text"}]}]},{type:"acc-item",sections:[{sub:[{type:"heading-1",text:"Heading Text"},{type:"ordered-item",text:"Item 1"},{type:"unordered-item",text:"Item 2"}]}]},{type:"acc-item",sections:[{sub:[{type:"heading-1",text:"Heading Text 2"}]}]}]}},types=[["list",["ordered-item","unordered-item"]],["accordion",["acc-item"]]];
var result = contentData.data.content.reduce((r, o) => {
var type = (types.find(({ 1: values }) => values.indexOf(o.type) > -1)|| {})[0];
if (!type) {
r.push(o);
group = undefined;
return r;
}
if (!group || group.type !== type) {
group = { type, items: [] };
r.push(group);
}
group.items.push(o);
return r;
}, []);
document.body.innerHTML = '<pre>' + JSON.stringify(result, null, ' ') + '</pre>';
You could store the last items array as well as the last embedded array and use them until a column type is found.
var contentData = { data: { content: [{ type: "column", sections: [{ sub: [{ type: "heading-1", text: "Heading Text" }] }] }, { type: "acc-item", sections: [{ sub: [{ type: "heading-1", text: "Heading Text" }, { type: "ordered-item", text: "Item 1" }, { type: "unordered-item", text: "Item 2" }] }] }, { type: "acc-item", sections: [{ sub: [{ type: "heading-1", text: "Heading Text 2" }] }] }] } },
list = ["ordered-item", "unordered-item"],
lastItems, lastEmbedded,
result = contentData.data.content.reduce((r, { type, sections }) => {
if (type === 'column') {
r.push({ type, embedded: sections.reduce((q, { sub }) => q.concat(sub), []) });
lastItems = undefined;
lastEmbedded = undefined;
return r;
}
if (!lastItems) r.push({ type: "acc-group", items: lastItems = [] });
lastItems.push(...sections.map(({ sub }) => ({
type,
embedded: sub.reduce((q, o) => {
if (list.includes(o.type)) {
if (!lastEmbedded) q.push({ type: 'list', items: lastEmbedded = [] });
lastEmbedded.push(o);
} else {
q.push(o);
lastEmbedded = undefined;
}
return q;
}, [])
})));
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The Array.prototype and Object.prototype methods are perfect for this kind of thing.
And you're right that this is some complicated kind of logic.
I would suggest that you definitely need some unit tests for this, and try break in to separate pieces.
Here's how I'm thinking I'd do it.
1. Group By the type to create your groups..
I'm actually creating a more generic solution that you've asked for here. That is, I'm not just grouping the 'acc-item', but everything.
I did a quick search for 'array group by javascript' and it gives us this answer which suggests using Array.reduce, so let's do that.
const groupedData = contentData.data.content.reduce((acc, cur) => {
//Check if this indexed array already exists, if not create it.
const currentArray = (acc[`${cur.type}-group`] && acc[`${cur.type}-group`].items) || [];
return {
...acc,
[`${cur.type}-group`]: {
type: `${cur.type}-group`,
items: [...currentArray, cur]
}
}
}, {});
2. Now for each of those items, we need to look at their subs, and group just the list items.
To do this, we basically want to find all the `item -> sections -> sub -> types and filter them into two arrays. A quick google on how to create two arrays using a filter gives me this answer.
First though, we need to flatten that sections-> subs thing, so lets just do that.
function flattenSectionsAndSubs(item) {
return {
type: item.type,
subs: item.sections.reduce((acc, cur) => ([...acc, ...cur.sub]), [])
};
}
And I'll just copy paste that partition function in:
function partition(array, isValid) {
return array.reduce(([pass, fail], elem) => {
return isValid(elem) ? [[...pass, elem], fail] : [pass, [...fail, elem]];
}, [[], []]);
}
const listTypes = ['ordered-item', 'unordered-item'];
function createEmbeddedFromItem(item) {
const [lists, nonLists] = partition(item.subs, (v) => listTypes.includes(v.type);
return {
type: item.type,
embedded: [
...nonLists,
{
type: "list",
items: lists
}
]
}
}
Putting this all together and we get.
const contentData = {
data: {
content: [{
type: "column",
sections: [{
sub: [{
type: "heading-1",
text: "Heading Text"
}]
}]
},
{
type: "acc-item",
sections: [{
sub: [{
type: "heading-1",
text: "Heading Text"
},
{
type: "ordered-item",
text: "Item 1"
},
{
type: "unordered-item",
text: "Item 2"
}
]
}]
},
{
type: "acc-item",
sections: [{
sub: [{
type: "heading-1",
text: "Heading Text 2"
}]
}]
}
]
}
}
function partition(array, isValid) {
return array.reduce(([pass, fail], elem) => {
return isValid(elem) ? [
[...pass, elem], fail
] : [pass, [...fail, elem]];
}, [
[],
[]
]);
}
function flattenSectionsAndSubs(item) {
return {
type: item.type,
subs: item.sections.reduce((acc, cur) => ([...acc, ...cur.sub]), [])
};
}
const listTypes = ['ordered-item', 'unordered-item'];
function createEmbeddedFromItem(item) {
const [lists, nonLists] = partition(item.subs, (v) => listTypes.includes(v.type));
return {
type: item.type,
embedded: [
...nonLists,
{
type: "list",
items: lists
}
]
}
}
const groupedData = contentData.data.content.reduce((acc, cur) => {
//Check if this indexed array already exists, if not create it.
const currentArray = (acc[`${cur.type}-group`] && acc[`${cur.type}-group`].items) || [];
const flattenedItem = flattenSectionsAndSubs(cur);
const embeddedItem = createEmbeddedFromItem(flattenedItem);
return {
...acc,
[`${cur.type}-group`]: {
type: `${cur.type}-group`,
items: [...currentArray, embeddedItem]
}
}
}, {});
console.log(groupedData);
Now this doesn't exactly match what you've asked for - but it should probably work.
You can add your own bits into only add a list item, if the array isn't empty, and to stop the column from being in its own group.
The thing is - tbh it seems like a little bit of a red flag that you would create an array of items that don't having matching structures, which is why I've done it this way.

How to build tree array from flat array of object with category and subCategrie properties

I am trying to build tree array from flat array, each item in the flat array has two property need to be used to build the tree array, they are 1. category. 2. subCategrie which is array of string.
let data = [
{
id: 1,
name: "Zend",
category: "php",
subCategory: ["framework"]
},
{
id: 2,
name: "Laravel",
category: "php",
subCategory: ["framework"]
},
{
id: 3,
name: "Vesion 5",
category: "php",
subCategory: ["versions"]
},
{
id: 4,
name: "Angular",
category: "frontend",
subCategory: ["framework", "typescript"]
},
{
id: 5,
name: "Aurelia",
category: "frontend",
subCategory: ["framework", "typescript"]
},
{
id: 6,
name: "JQuery",
category: "frontend",
subCategory: []
}
];
It should be
let tree = [
{
name: "php",
children: [
{
name: "framework",
children: [
{
id: 1,
name: "Zend"
},
{
id: 2,
name: "Laravel"
}
]
},
{
name: "versions",
children: [
{
id: 3,
name: "Vesion 5"
}
]
}
]
}
// ...
];
Is there any article, link solving similar problem?
I gave it many tries but stuck when trying to build the sub categories children.
Here's my last attempt which throws error and I know it's wrong but it's for the ones who want to see my attempts
const list = require('./filter.json')
let tree = {};
for (let filter of list) {
if (tree[filter.category]) {
tree[filter.category].push(filter);
} else {
tree[filter.category] = [filter];
}
}
function buildChildren(list, subcategories, category, index) {
let tree = {}
for (let filter of list) {
if (filter.subcategory.length) {
for (let i = 0; i < filter.subcategory.length; i++) {
let branch = list.filter(item => item.subcategory[i] === filter.subcategory[i]);
branch.forEach(item =>{
if (tree[filter.subcategory[i]]){
tree[filter.subcategory[i]] = tree[filter.subcategory[i]].push(item)
}else{
tree[item.subcategory[i]] = [item]
}
})
}
}
}
console.log('tree ', tree);
}
Heads up, For javascript I usually use Lodash (usually written as _ in code) but most of these methods should also be built in to the objects in javascript (i.e. _.forEach = Array.forEach())
const tree = [];
// First Group all elements of the same category (PHP, Frontend, etc.)
data = _.groupBy(data, 'category');
_.forEach(data, function (categoryElements, categoryName) {
// Each Category will have it's own subCategories that we will want to handle
let categorySubCategories = {};
// The categoryElements will be an array of all the objects in a given category (php / frontend / etc..)
categoryElements.map(function (element) {
// For each of these categoryies, we will want to grab the subcategories they belong to
element.subCategory.map(function (subCategoryName) {
// Check if teh category (PHP) already has already started a group of this subcategory,
// else initialize it as an empty list
if (!categorySubCategories[subCategoryName]) { categorySubCategories[subCategoryName] = []; }
// Push this element into the subcategory list
categorySubCategories[subCategoryName].push({id: element.id, name: element.name});
});
});
// Create a category map, which will be a list in the format {name, children}, created from
// our categorySubCategories object, which is in the format {name: children}
let categoryMap = [];
_.forEach(categorySubCategories, function (subCategoryElements, subCategoryName) {
categoryMap.push({name: subCategoryName, children: subCategoryElements});
});
// Now that we've grouped the sub categories, just give the tree it's category name and children
tree.push({name: categoryName, children: categoryMap});
});
};
The key to success here is to create an interim format that allows for easy lookups. Because you work with children arrays, you end up having to use filter and find whenever you add something new, to prevent duplicates and ensure grouping.
By working with a format based on objects and keys, it's much easier to do the grouping.
We can create the groups in a single nested loop, which means we only touch each item once for the main logic. The group has this format:
{ "categoryName": { "subCategoryName": [ { id, name } ] } }
Then, getting to the required { name, children } format is a matter of one more loop over the entries of this tree. In this loop we move from { "categoryName": catData } to { name: "categoryName", children: catData }
Here's an example that shows the two steps separately:
const data=[{id:1,name:"Zend",category:"php",subCategory:["framework"]},{id:2,name:"Laravel",category:"php",subCategory:["framework"]},{id:3,name:"Vesion 5",category:"php",subCategory:["versions"]},{id:4,name:"Angular",category:"frontend",subCategory:["framework","typescript"]},{id:5,name:"Aurelia",category:"frontend",subCategory:["framework","typescript"]},{id:6,name:"JQuery",category:"frontend",subCategory:[]}];
// { category: { subCategory: [ items ] } }
const categoryOverview = data.reduce(
(acc, { id, name, category, subCategory }) => {
// Create a top level group if there isn't one yet
if (!acc[category]) acc[category] = {};
subCategory.forEach(sc => {
// Create an array for this subCat if there isn't one yet
acc[category][sc] = (acc[category][sc] || [])
// and add the current item to it
.concat({ id, name });
});
return acc;
},
{}
)
const nameChildrenMap = Object
.entries(categoryOverview)
// Create top level { name, children } objects
.map(([cat, subCats]) => ({
name: cat,
children: Object
.entries(subCats)
// Create sub level { name, children } objects
.map(([subCat, items]) => ({
name: subCat,
children: items
}))
}))
console.log(nameChildrenMap);

Normalize API data for use in Redux

I have the following data coming from an API:
const data = {
id: 1,
name: 'myboard',
columns: [
{
id: 1,
name: 'col1',
cards: [
{ id: 1, name: 'card1' },
{ id: 2, name: 'card2' }
]
},
{
id: 2,
name: 'col2',
cards: [
{ id: 3, name: 'card3' },
{ id: 4, name: 'card4' }
]
},
]
}
As you can see, there are essentially 3 nested levels. The top level contains an id, name and a list of columns. Each column contains an id, name and a list of cards. Each card has an id and name.
I wish to normalize the data for use in Redux as presented here. I am using normalizr to do this as follows:
const card = new schema.Entity('cards');
const column = new schema.Entity('columns', {
cards: [card]
});
const board = new schema.Entity('boards', {
columns: [column]
});
normalize(data, board)
This results in the following:
{
"entities": {
"cards": {
"1": {
"id": 1,
"name": "card1"
},
"2": {
"id": 2,
"name": "card2"
},
"3": {
"id": 3,
"name": "card3"
},
"4": {
"id": 4,
"name": "card4"
}
},
"columns": {
"1": {
"id": 1,
"name": "col1",
"cards": [1, 2]
},
"2": {
"id": 2,
"name": "col2",
"cards": [3, 4]
}
},
"boards": {
"1": {
"id": 1,
"name": "myboard",
"columns": [1, 2]
}
}
},
"result": 1
}
What I can't seem to figure out is how to have each section (ie: cards, columns, boards) split into two sections, namely byId and allIds as per the Redux article referenced above.
Essentially this is to make ordering, sorting etc easier in a React application. Im using the latest version of normalizr (3.2.4).
Here is a CodeSandbox with an example of how you can set up the reducers to handle the normalized state.
Essentially, you will end up with something like this for each of your entities:
// lambda or function - whatever your preference is
const cardsById = (state = {}, action) => {
// if, case, handler function - whatever your preference is
if (action.type === 'ADD_DATA') { // or whatever your initial data load type is
return { ...state, ...action.payload.cards }
}
return state
}
const allCards = (state = [], action) => {
if (action.type === 'ADD_DATA') { // or whatever your initial data load type is
return [...state, ...Object.keys(action.payload.cards)]
}
return state
}
const cards = combineReducers({
byId: cardsById,
allIds: allCards
})
and then combine all of those together:
export default combineReducers({
cards,
columns,
boards
})
The action creators for this are as follows:
const addData = ({ entities }) => ({
type: 'ADD_DATA',
payload: entities // note the rename - this is not required, just my preference
})
// I used a thunk, but theory is the the same for your async middleware of choice
export const getData = () => dispatch => dispatch(addData(normalize(data, board)))
Hope this helps. Remember that you will need to maintain both the byId and allIds for each entity as entities are added or removed.

Categories

Resources