I'm just starting with immutable.js and I'm having trouble figuring out how to set a new property on objects within an array. I'm having trouble finding any examples in the docs of this kind of change.
I'm basically just trying to take change this:
[{
gitInfo: {id: 8001, host: '', …},
module: {id: 24875, name: "blah", …}
}...]
to this:
[{
gitInfo: {id: 8001, host: '', …},
module: {id: 24875, name: "blah", isStared: true …}
}...]
So w/out immutable.js I would have something like:
function markModules(modules) {
modules.map( (module) => {
module.module.isStarred = false;
if (contains(this.props.stars, module.module.id)) {
module.module.isStarred = true;
}
})
return modules;
}
I'm assuming I need something like set() with a List, but again, I'm not finding any examples how to do this.
Thanks for any tips or links to examples.
You'd do it the same way you would without Immutable.js (.map).
const data = Immutable.fromJS([{
gitInfo: {id: 8001, host: ''},
module: {id: 24875, name: "blah"}
}, {
gitInfo: {id: 6996, host: ''},
module: {id: 666, name: "wef"}
}]);
const transformed = data.map((x) => {
return x.get('module').set('isStarred', true);
})
transformed.toJS() === [
{
"id": 24875,
"name": "blah",
"isStarred": true
},
{
"id": 666,
"name": "wef",
"isStarred": true
}
]
And if you want to put the extra logic in there:
function markModules(modules) {
return modules.map((module) => {
const isStarred = contains(this.props.stars, module.getIn(['module', 'id']));
return module.setIn(['module', 'isStarred'], isStarred);
})
}
The key is to turn your if statements into values/functions that return values instead of updating the data structure.
Related
I have following state:
merchant: [{
title: "Setup",
steps: [
{id: "provider", done: false},
{id: "api_key", done: false}
{id: "client", done: false}
]
}]
and i want to update it with the following dataset
merchant: [{
title: "Setup",
steps: [
{id: "provider", done: false},
{id: "api_key", done: true}
]
}]
So that I end up with the following:
merchant: [{
title: "Setup",
steps: [
{id: "provider", done: false},
{id: "api_key", done: true}
{id: "client", done: false}
]
}]
What would be the cleanest way to achieve this?
I've done something like this in my reducer, but it seems like a terrible idea based on the output I'm getting.
guide_progression: {
...state.guide_progression,
merchant: state.guide_progression.merchant.map(stateGuide =>
payload.user.guide_progression.merchant.map(userGuide =>
userGuide.title === stateGuide.title &&
{
...stateGuide,
steps: stateGuide.steps.map(stateStep =>
userGuide.steps.map(userStep =>
userStep.id === stateStep.id &&
{
...stateStep,
done: userStep.done
}
)
)
}
)
)
}
Really appreciate suggestions for how to solve this. I've been struggling to find a good solution on the web.
You can use Immer, It allows you to
Create the next immutable state tree by simply modifying the current tree
Basically allows you to modify your data while keeping it immutable.
So immer is straight up amazing. Thanks ahmed mahmoud. Here's the solution I ended up with using immer.js
updateMerchantState = produce(state.guide_progression.merchant, draft => {
payload.user.guide_progression.merchant.map(userGuide => {
const guideIndex = draft.findIndex(guide => guide.title === userGuide.title)
if (guideIndex !== -1) {
userGuide.steps.map(userStep => {
const stepIndex = draft[guideIndex].steps.findIndex(step => step.id === userStep.id)
if (stepIndex !== -1) draft[guideIndex].steps[stepIndex].done = userStep.done
})
}
})
})
I do a simple explorer on Angular (there are a list of directories that contain other directories or text files). The question is: I receive the following data from the server ("path" is the path of the folder, ids of parent directories):
[
{
id: "6np5E3yyEISXLNX9muyt",
name: "sec list",
path: ["", "GnBOclNO1v3n9FW7aGv0", "X5YNJ6Vco2BtGxNZVsYV"],
},
{
id: "GnBOclNO1v3n9FW7aGv0",
name: "In aeroport",
path: [""],
},
{
id: "H6AvpwXc49v4oDRWSjym",
name: "Delete",
path: [""],
},
{
id: "LQ73vVoTuw9xd40jMs3j",
name: "Aeroport list",
path: [""],
},
{
id: "X5YNJ6Vco2BtGxNZVsYV",
name: "Bordery words",
path: ["", "GnBOclNO1v3n9FW7aGv0"],
},
{
id: "jWeClRAw55Er8z0Ow9uq",
name: "mail list",
path: ["", "GnBOclNO1v3n9FW7aGv0", "X5YNJ6Vco2BtGxNZVsYV"],
}
];
How can I regroup that into code below? I know recursion is needed, but I can not understand, how to do it right. Help me, please.
[
{
id: "GnBOclNO1v3n9FW7aGv0",
name: "In aeroport",
children: [
{
id: "X5YNJ6Vco2BtGxNZVsYV",
name: "Bordery words",
children: [
{
id: "6np5E3yyEISXLNX9muyt",
name: "sec list",
},
{
id: "jWeClRAw55Er8z0Ow9uq",
name: "mail list",
}
],
}
],
},
{
id: "H6AvpwXc49v4oDRWSjym",
name: "Delete",
},
{
id: "LQ73vVoTuw9xd40jMs3j",
name: "Aeroport list",
},
]
Simple DFS solves the problem, but there are multiple ways to do this. One way is below
var paths = [
{
id: "6np5E3yyEISXLNX9muyt",
name: "sec list",
path: ["", "GnBOclNO1v3n9FW7aGv0", "X5YNJ6Vco2BtGxNZVsYV"],
},
{
id: "GnBOclNO1v3n9FW7aGv0",
name: "In aeroport",
path: [""],
},
{
id: "H6AvpwXc49v4oDRWSjym",
name: "Delete",
path: [""],
},
{
id: "LQ73vVoTuw9xd40jMs3j",
name: "Aeroport list",
path: [""],
},
{
id: "X5YNJ6Vco2BtGxNZVsYV",
name: "Bordery words",
path: ["", "GnBOclNO1v3n9FW7aGv0"],
},
{
id: "jWeClRAw55Er8z0Ow9uq",
name: "mail list",
path: ["", "GnBOclNO1v3n9FW7aGv0", "X5YNJ6Vco2BtGxNZVsYV"],
}
];
var dfs = function( parentJson , path){
for(var i=0;i<paths.length;i++){
if(paths[i].path.join("") == path ){
var child = {id:paths[i].id,name:paths[i].name,children:[]}
parentJson.push(child)
dfs(child.children,path+paths[i].id)
}
}
}
var json = [];
dfs(json,"")
console.log(json)
You could do this in two phases:
First make a nested object structure where the children properties are not arrays, but objects, where the object keys are the id values. That way you can quickly navigate in that structure with a given path, and extend/deepen it at the same time.
In a final phase, you can use recursion to walk through that tree structure to convert those children properties to arrays.
Here is how that looks:
function makeTree(data) {
// drill down the object structure, where children properties
// are nested objects with id values as keys.
let result = {}; // root of the tree data structure
for (let {id, name, path} of data) {
Object.assign(path.slice(1).concat(id).reduce((acc, key) => {
if (!acc.children) acc.children = {};
if (!acc.children[key]) acc.children[key] = {};
return acc.children[key];
}, result), { id, name });
}
return (function unkey(node) {
// Convert children objects to arrays
if (node.children) node.children = Object.values(node.children).map(unkey);
return node;
})(result);
}
let data = [{id: "6np5E3yyEISXLNX9muyt",name: "sec list",path: ["", "GnBOclNO1v3n9FW7aGv0", "X5YNJ6Vco2BtGxNZVsYV"],},{id: "GnBOclNO1v3n9FW7aGv0",name: "In aeroport",path: [""],},{id: "H6AvpwXc49v4oDRWSjym",name: "Delete",path: [""],},{id: "LQ73vVoTuw9xd40jMs3j",name: "Aeroport list",path: [""],},{id: "X5YNJ6Vco2BtGxNZVsYV",name: "Bordery words",path: ["", "GnBOclNO1v3n9FW7aGv0"],},{id: "jWeClRAw55Er8z0Ow9uq",name: "mail list",path: ["", "GnBOclNO1v3n9FW7aGv0", "X5YNJ6Vco2BtGxNZVsYV"],}];
console.log(makeTree(data));
Note that the first value of the path value is never used. It seems to always be the empty string.
I am using some assignment destructuring in my MongoDB/Node backend in order to handle some post-processing. I'm just trying to understand how this destructuring works, and if, in the case of an array of multiple elements and nested arrays, if I can input the element I want to target.
Take for instance this code:
services: [
,
{
history: [...preSaveData]
}
]
} = preSaveDocObj;
My assumption is that the "," in "services" for the above code will default to looking at the first element in the array. Correct?
Now, if I have a document structure that looks like this (see below), and I know I want to target the "services" element where "service" is equal to "typeTwo", how would I do that?:
{
_id: 4d39fe8b23dac43194a7f571,
name: {
first: "Jane",
last: "Smith"
}
services: [
{
service: "typeOne",
history: [
{ _id: 121,
completed: true,
title: "rookie"
},
{ _id: 122,
completed: false,
title: "novice"
}
]
},
{
service: "typeTwo",
history: [
{ _id: 135,
completed: true,
title: "rookie"
},
{ _id: 136,
completed: false,
title: "novice"
}
]
}
]
}
How can I edit this code (see below) to specifically target the "services" array where "service" is equal to "typeTwo"?
services: [
,
{
history: [...preSaveData]
}
]
} = preSaveDocObj;
Don't overdestructure, just find:
const { history: [...preSavedData] } = doc.services.find(it => it.serice === "typeTwo");
I have something like this :
let user = [
{
name: "step-one",
values: {companyName: "Name", address: "company address"}
},
{
name: "step-two",
values: {name: "User", mobile: 0123}
},
{
name: "step-three",
values: [
{file: "companyLogo", values: {active: true, fileName: "some name"}},
{file: "avatar", values: {active: true, fileName: "file name"}}
]
}
]
I want to get only values and put them into a new object. Thus, something like :
let wantedResult = {
companyName: "Name",
address: "company address",
name: "User",
mobile: 0123,
files: [
{file: "companyLogo", values: {active: false, fileName: "some name"}},
{file: "avatar", values: {active: false, fileName: "file name"}}
]
};
Any advice how I can do that?
You can try this!
let user = [{
name: "step-one",
values: {
companyName: "Name",
address: "company address"
}
}, {
name: "step-two",
values: {
name: "User",
mobile: 0123
}
}, {
name: "step-three",
values: [{
file: "companyLogo",
values: {
active: true,
fileName: "some name"
}
}, {
file: "avatar",
values: {
active: true,
fileName: "file name"
}
}]
}]
var wantedResult = Object.assign({}, user[0].values, user[1].values, {files: user[2].values})
console.log(wantedResult)
It is a bit hacky because of the files array, but i would do it like that:
var wantedResult = user.reduce((result, step) => {
var values = Array.isArray(step.values) ? { files: step.values } : step.values;
return Object.assign({}, result, values)
}, {});
Of course it'll work only for that kind of structure you provided. If you have more objects that have an array in the 'values' property, you'll need to rethink the approach.
Step 3 is inconsistent with the others. If possible, make it consistent to have: values: files: [ { file1_data }, { file2_data } ].
After fixing the inconsistency, you can iterate through each of the steps and add the new properties to the result.
let wantedResult = user.reduce(function(result, spec) {
Object.keys(spec.values).forEach(function(key) {
result[key] = spec.values[key];
});
return result;
}, {});
If not possible to change step 3, you can make it a bit less clean:
let wantedResult = user.reduce(function(result, spec) {
if (spec.name === "step-three") {
result.files = spec.values;
}
else {
Object.keys(spec.values).forEach(function(key) {
result[key] = spec.values[key];
});
}
return result;
}, {});
I googled some examples and tutorials but couldn't find any clear example for my case.
I get a JSON response from my server like this:
var heroes = [
{
id: 5,
name: 'Batman',
realName: 'Bruce Wayne',
equipments: [
{
type: 'boomarang',
name: 'Batarang',
},
{
type: 'cloak',
name: 'Bat Cloak',
},
{
type: 'bolas',
name: 'Bat-Bolas',
}
]
},
{
id: 6,
name: 'Cat Woman',
realName: 'Selina Kyle',
equipments: [
{
type: 'car',
name: 'Cat-illac',
},
{
type: 'bolas',
name: 'Cat-Bolas',
}
]
}
];
I would like to query for example: "get heroes with equipment type of bolas"
and It should return both hero objects in an array.
I know it is not right but what I am trying to do is to form a map function like this:
function myMapFunction(doc) {
if(doc.equipments.length > 0) {
emit(doc.equipment.type);
}
}
db.query(myMapFunction, {
key: 'bolas',
include_docs: true
}).then(function(result) {
console.log(result);
}).catch(function(err) {
// handle errors
});
Is it possible? If not what alternatives do I have?
P.S: I also checked LokiJS and underscoreDB. However PouchDB looks more sophisticated and capable of such query.
Thank you guys in advance
Your map function should be:
function myMapFunction(doc) {
doc.equipments.forEach(function (equipment) {
emit(equipment.type);
});
}
Then to query, you use {key: 'bolas'}:
db.query(myMapFunction, {
key: 'bolas',
include_docs: true
}).then(function (result) {
// got result
});
Then your result will look like:
{
"total_rows": 5,
"offset": 0,
"rows": [
{
"doc": ...,
"key": "bolas",
"id": ...,
"value": null
},
{
"doc": ...,
"key": "bolas",
"id": ...,
"value": null
}
]
}
Also be sure to create an index first! Details are in the PouchDB map/reduce guide :)