Possible duplicate:
Add an Item in OrderedMap with Immutable.js
Working with redux store and Immutable js OrderedMap.
Redux store structure:
{
"app": {
"name": "Test app",
"dataPack":{
"chemID": "aaaa",
"item": {
"yp57m359": {
"solid": 33,
"liquid": 45,
"gas": 65
},
"h58wme1y": {
"solid": 87,
"liquid": 9,
"gas": 30
},
"dff56fhh": {
"solid": 76,
"liquid": 43,
"gas": 77
}
}
}
}
}
Reducer code:
return state.setIn(["app","dataPack","item",action.item_id],
fromJS({
"solid": action.soildVal,
"liquid": action.liquidVal,
"gas": action.gasVal
}));
where action.item_id is the random id (key for every item).
Above code works perfectly for adding items.
Problem is: Items stored in a random position. I need to keep the order I am adding. Need to add every item as last entry inside item. Adding one by one item is not in same order.
Help me to get a clear solution for this.
An OrderedMap will remember the order you put things in it. Every time you call .set(key, value) with a new key on an OrderedMap, it will get added to the end.
let state = Immutable.Map({
"app": Immutable.Map({
"name": "Test App",
"dataPack": Immutable.Map({
"chemId": "aaaa",
"items": Immutable.OrderedMap({}) // this is the thing we want ordered
})
})
});
state = state.setIn(['app', 'dataPack', 'items', 'yp57m359'], Immutable.Map({
'first': 'item'
}));
state = state.setIn(['app', 'dataPack', 'items', 'h58wme1y'], Immutable.Map({
'second': 'item'
}));
state = state.setIn(['app', 'dataPack', 'items', 'dff56fhh'], Immutable.Map({
'third': 'item'
}));
console.log(state.toJS()); // notice things are in order
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.2/immutable.js"></script>
It's hard to tell exactly where your problem is because we can't see how you created your store, but my guess would be that "item" is pointing to a regular old Map instead of an OrderedMap. If you're just using fromJS(data) to create your state, it will default to using a Map instead of an OrderedMap.
Related
I have an object like this:
const objBefore:
{
"id": "3pa99f64-5717-4562-b3fc-2c963f66afa1",
"number": "5000",
"enabled": true,
"classes": [
{
"id": "2fc87f64-5417-4562-b3fc-2c963f66afa4",
"name": "General"
},
{
"id": "7ffcada8-0215-4fb0-bea9-2266836d3b18",
"name": "Special"
},
{
"id": "6ee973f7-c77b-4738-b275-9a7299b9b82b",
"name": "Limited"
}
]
}
Using es6, I want to grab everything in the object except the name key of the inner classes array to pass it to an api.
So:
{
"id": "3pa99f64-5717-4562-b3fc-2c963f66afa1",
"number": "5000",
"enabled": true,
"classes": [
{"id": "2fc87f64-5417-4562-b3fc-2c963f66afa4"},
{"id": "7ffcada8-0215-4fb0-bea9-2266836d3b18"},
{"id": "6ee973f7-c77b-4738-b275-9a7299b9b82b"}
]
}
The closest I got was: let {id, number, enabled, classes: [{id}]} = objBefore;
But it only gets me one id in classes. I've tried spreading above using [...{id}] or [{...id}]. Same thing.
I find it challenging to get the right mental model for how to think about this when it's on multiple levels. In my mind, when I say [...{id}] I'm thinking, "I want the id property as an object in the outer classes array, but give me every id in the array!"
Clearly I'm not thinking about this correctly.
I've tried it using map to get that part but I'm still having trouble combining it back to the original to produce the desired result. for example:
let classIds = objBefore.classes.map(({id}) => {
return {
id
}
})
(Using the map syntax, how can I destructure in the function the other keys that are one level higher?)
To combine them I started trying anything and everything, :
let {id, number, enabled, classIds} = {objBefore, [...classIds]} // returns undefined for all
I'd prefer to do it in one statement. But if that's not possible, then what's a clean way to do it using map?.
You can't destructure and map at the same time in the way you're looking to do it. The main purpose of destructuring assignment is to extract data from an array/object and not for manipulating data. In your case, as you're after an object with the same keys/value as your original object, just with a different classes array, I would instead suggest creating a new object and spreading ... the original object into that. Then you can overwrite the classes array with a mapped version of that array:
const objBefore = { "id": "3pa99f64-5717-4562-b3fc-2c963f66afa1", "number": "5000", "enabled": true, "classes": [ { "id": "2fc87f64-5417-4562-b3fc-2c963f66afa4", "name": "General" }, { "id": "7ffcada8-0215-4fb0-bea9-2266836d3b18", "name": "Special" }, { "id": "6ee973f7-c77b-4738-b275-9a7299b9b82b", "name": "Limited" } ] };
const newObj = {
...objBefore,
classes: objBefore.classes.map(({id}) => ({id}))
};
console.log(newObj);
How about using simple util method with object destructuring, spread operator and map
const objBefore = {
id: "3pa99f64-5717-4562-b3fc-2c963f66afa1",
number: "5000",
enabled: true,
classes: [
{
id: "2fc87f64-5417-4562-b3fc-2c963f66afa4",
name: "General",
},
{
id: "7ffcada8-0215-4fb0-bea9-2266836d3b18",
name: "Special",
},
{
id: "6ee973f7-c77b-4738-b275-9a7299b9b82b",
name: "Limited",
},
],
};
const process = ({ classes, ...rest }) => ({
...rest,
classes: classes.map(({ id }) => ({ id })),
});
console.log(process(objBefore))
In one line, you could do this:
const objAfter = { ...objBefore, classes: objBefore.classes.map(item => ({ id: item.id })) };
Or, if you prefer:
const objAfter = {...objBefore, classes: objBefore.classes.map(({id}) => ({id}))};
There isn't any way in object destructing to copy an entire array of objects into a different array of objects by removing properties so you use .map() for that.
I want to dynamically build this object with the data below.
var object1 = {
machines: [
{
node: "01",
ram: "8",
disks: [ {sdasize: '20'}, {sdbsize: '200'} ],
mounts: [ {mount: "/var/lib", size: "10"} , {mount: "/home", size: "140"}]
},
{
node: "02",
ram: "8",
disks: [ {sdasize: '75'}, {sdbsize: '300'} ],
mounts: [ {mount: "/var/log", size: "20"} , {mount: "/var/www/html", size: "200"}]
}
]
};
I want to be able to add new machines and make changes to any value or add more mounts to the array that is nested. I have been looking at this for some time with no solution as of yet.
You can do this:
var object1 = {machines: []}
function addMachines(node,ram,disks,mounts){
object1.machines.push({node:node,ram:ram,disks:disks,mounts:mounts})
}
Since machines is a array we can use push() to insert objects inside that array.
Now you can use addMachines function in a loop or based on eventlisteners, and build the object1 via parameters in that function
Edit:
As commented below you can also add a function to build your other objects, like disks and mount.
buildDisk(sdasize,sdbsize)
{
return [{sdasize:sdasize},{sdbsize:sdbsize}]
}
Usage:
addMachines('01','8',buildDisk(20,50),'nice')
what object1 would look like
machines:[
{
node:'01',
ram:'8',
disks:[
{
sdasize:20,
sdbsize:50
},
mounts:'nice'
}];
I want to fetch all the names and label from JSON without loop. Is there a way to fetch with any filter method?
"sections": [
{
"id": "62ee1779",
"name": "Drinks",
"items": [
{
"id": "1902b625",
"name": "Cold Brew",
"optionSets": [
{
"id": "45f2a845-c83b-49c2-90ae-a227dfb7c513",
"label": "Choose a size",
},
{
"id": "af171c34-4ca8-4374-82bf-a418396e375c",
"label": "Additional Toppings",
},
],
},
]
}
When you say "without loops" I take it as without For Loops. because any kind of traversal of arrays, let alone nested traversal, involve iterating.
You can use the reduce method to have it done for you internally and give you the format you need.
Try this :
const data = {
sections: [
{
id: "62ee1779",
name: "Drinks",
items: [
{
id: "1902b625",
name: "Cold Brew",
optionSets: [
{
id: "45f2a845-c83b-49c2-90ae-a227dfb7c513",
label: "Choose a size"
},
{
id: "af171c34-4ca8-4374-82bf-a418396e375c",
label: "Additional Toppings"
}
]
}
]
}
]
};
x = data.sections.reduce((acc, ele) => {
acc.push(ele.name);
otherName = ele.items.reduce((acc2, elem2) => {
acc2.push(elem2.name);
label = elem2.optionSets.reduce((acc3, elem3) => {
acc3.push(elem3.label);
return acc3;
}, []);
return acc2.concat(label);
}, []);
return acc.concat(otherName);
}, []);
console.log(x);
Go ahead and press run snippet to see if this matches your desired output.
For More on info reduce method
In the context of cJSON
yes, we can fetch the key value for any of the object.
1 - each key value is pointed by one of the objects. will simply fetch that object and from there will get the key value.
In the above case for
pre-requisition: root must contain the json format and root must be the cJSON pointer. if not we can define it and use cJSON_Parse() to parse the json.
1st name object is "sections" will use
cJSON *test = cJSON_GetObjectItem(root, "sections");
char *name1 = cJSON_GetObjectItem(test, "name" )->valuestring;
2nd name key value
cJSON *test2 = cJSON_GetObjectItem(test, "items");
char *name2 = cJSON_GetObjectItem(tes2, "name")->valuestring;
likewise, we can do for others as well to fetch the key value.
I am trying to use underscoreJs to manipulate a JavaScript object and having problems doing so.
Here is my example
var data = {
"label": "SomeName",
"parent": [{
"id": "parentId",
"resources": [{
"name": "ID1NAME",
"calls": [
"user_get", "user2_post", "user3_delete"
]
}, {
"name": "ID2",
"calls": [
"employee1_get", "employee2_delete", "employee3_update"
]
}]
}]
};
var res = _(data).chain().
pluck('parent').
flatten().
findWhere(function(item){
item === "user_get"
}).
value();
console.log(res);
Using an element which is a part of data.parent.calls[] (example : "user_get") I would like to extract its parent object, i.e. data.parent[0].
I tried above but always get undefined. I appreciate any help on this.
One of the problems you're having is your use of _.pluck. If you execute _.pluck over an object, it'll go over the keys of the object trying to retrieve the property you specified as the second argument (in this case, 'parent'). 'label' is a string and 'parent' is an array so thus the array that you get as a result is [undefined, undefined]. The rest will then go wrong.
One solution could be as follows:
function findCallIndexInParent(call, parent) {
return _.chain(parent)
.pluck('resources')
.flatten()
.findIndex(function (obj) {
return _.contains(obj.calls, call);
})
.value();
}
function findCall(call, data) {
var parent = data.parent;
return parent[findCallIndexInParent(call, parent)];
}
console.log(findCall('user_get', data));
findCall is just a convenient method that will pass the parent property of data to findCallIndexInParent (that will retrieve the index where call is) and return the desired object with the parent array.
Lodash (a fork of underscore) provides a method to get the property of an object that would have come really handy in here (sadly, underscore doesn't have it).
The explanation of findCallIndexInParent is as follows:
Chain the parent list
pluck the resources array
As pluck maps, it returns a list of lists so a flatten is needed.
Find the index of the element which calls contains call
Return the value (the index) of the object that contains call within parent.
Here's the fiddle. Hope it helps.
This would seem to do the trick.
function findByCall(data, call) {
return _.find(data.parent, function(parent) { //From data.parent list, find an item that
return _.some(parent.resources, function(resource) {//has such parent.resource that it
return _.includes(resource.calls, call); //includes the searched resource.calls item
});
});
}
//Test
var data = {
"label": "SomeName",
"parent": [{
"id": "parentId",
"resources": [{
"name": "ID1NAME",
"calls": [
"user_get", "user2_post", "user3_delete"
]
}, {
"name": "ID2",
"calls": [
"employee1_get", "employee2_delete", "employee3_update"
]
}]
}]
};
console.log(findByCall(data, 'user_get'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore.js"></script>
If I understand correctly, you want to get the index of the element in the parent array which has any resource with the specified call.
data = {
"label": "SomeName",
"parent": [{
"id": "parentId",
"resources": [{
"name": "ID1NAME",
"calls": [
"user_get", "user2_post", "user3_delete"
]
}, {
"name": "ID2",
"calls": [
"employee1_get", "employee2_delete", "employee3_update"
]
}]
}]
}
// find the index of a parent
const index = _.findIndex(data.parent, parent =>
// that has any (some) resources
_.some(parent.resources, resource =>
// that contains 'user_get' call in its calls list
_.contains(resource.calls, 'user_get')
)
)
console.log(index) // 0
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
If you want to find the actual parent object, use find instead of findIndex
If you want to find all parent objects matching this call, use filter instead of findIndex
I have a ReactJS component that renders a list of orders.
I get orders from a REST API. The format of the data is the following:
{
"count": 2,
"orders": [
{
"order_number": 55981,
"customer_number": 24742
},
{
"order_number": 55980,
"customer_number": 24055
}
]
}
Each order can have a list of items. When I click on an order, I get the list of items in the following format:
{
"count": 2,
"items": [
{
"name": "Green pillow",
"status": "pending"
},
{
"name": "Red pillow",
"status": "delivered"
}
]
}
The orders list is refreshed automatically and can change any time, so I store the orders list in this.state which gets updated via ajax.
this.state looks like this:
{
"orders": [
{
"order_number": 55981,
"customer_number": 24742
},
{
"order_number": 55980,
"customer_number": 24055
}
]
}
My problem is that I would like that, when I click on an order, the state gets updated so that the clicked order contains the items associated to that order. The order list would look like this after clicking on an item:
{
"count": 2,
"orders": [
{
"order_number": 55981,
"customer_number": 24742,
"items": [
{
"name": "Green pillow",
"status": "pending"
}
]
},
{
"order_number": 55980,
"customer_number": 24055
}
]
}
How can I add items to a specific order using this.setState()? The problem is that setState seem to update data using keys, but my orders are in an array. I can probably copy the whole array and put the items key inside, but that seems overkill.
Am I taking the wrong approach?
I'm not entirely sure I got your question, but I think that what you're trying to achieve is add new orders to an array (push) which is located in your state.
If that's the case, you should something like this:
// since you're orders it's not a plain array, you will have
// to deep clone it (e.g. with lodash)
let orders = _.clone(this.state.orders);
orders.push(newOrder);
this.setState(orders);
Why cloning the state before changing it is important?
Immutability comes with some great properties (like easy equality comparison) and the React team is aiming towards that direction to improve performance more and more.
As of React 0.13 mutating state without calling this.setState will trigger a warning, and it will break entirely at some point in React's future (see the docs)
Hope this helps
Make a copy of the current state, modify the copy and use it as the new state.
This is just a simple example. You might want to add some caching logic so you don't have to retrieve the order's item again and again when a user click on the same order multiple times.
var updatedOrder = _.clone(this.state.orders);
updatedOrder[0]["items"] = [ { "name": "Foo", "status":"bar" } ];
this.setState({
orders: updatedOrder
});