JSON array iteration skips first element - NodeJS - arrays

Ive got this JSON markup:
"Categories": [
{"name": "a", "id": "1"},
{"name": "b", "id": "2"},
{"name": "c", "id": "3"},
{"name":"d", "id": "4"},
{"name":"e", "id": "5"},
{"name": "f", "id": "6"},
{"name": "g", "id": "7"},
{"name": "h", "id": "8"}
]
Ive got a setInterval going over each of these categories and making a new Promise for each one.
For some unknown reason, it always skips the first element and exists with an out of bounds exception
var i = 0;
var id = setInterval(function(){
if (i == categories.length){
clearInterval(id);
}
client.itemSearch({
category: categories[i].id,
catName: categories.name,
}).then(function(results){
console.log("From category - " + categories[i].name + "\n" +
"Title: " + results[0].Title);
},function(err) {
console.log("error at " + categories[i].name);
});
i+=1;
}, 1000);

The problem is that you're referencing i in your promise callbacks and i changes every second. So your callbacks are being executed sometime in the future when i === categories.length, which would be an invalid index.
Since you already have a closure, just store the current category object in a variable outside of your promise callbacks and reference that instead.
You're also letting the function continue when i === categories.length which will cause an invalid index access.
Here's an example of a solution that fixes the aforementioned issues:
var i = 0;
var id = setInterval(function() {
if (i === categories.length) {
clearInterval(id);
return;
}
var category = categories[i];
client.itemSearch({
category: category.id,
catName: category.name,
}).then(function(results) {
console.log("From category - " + category.name + "\n" +
"Title: " + results[0].Title);
}, function(err) {
console.log("error at " + category.name);
});
i += 1;
}, 1000);
Lastly, you originally had catName: categories.name but I changed it to catName: category.name as I assumed that may have been your original intention.

Related

How to modify a complex JSON Object in using Immutable

I have below JSON and wanted to update the value depending on Aid, Bid and Cid using Immutable.js
e.g.
Below input provided.
Aid= A, Bid = 1, Cid= 4, NewValue = 'FOUR'
If above input is provided the value "One" needs to be changed to "FOUR"
let sampleJson = {
Aid: 'A', detail:"sample", list: [
{
"Bid": "1",
"group": [
{
"name": "Group A",
"Cid": "4",
"value": "One"
},
{
"name": "Group A",
"Cid": "41",
"value": "1"
},
]
},
{
"Bid": "2",
"group": [
{
"name": "Group A",
"Cid": "4",
"value": "1"
},
{
"name": "Group A",
"Cid": "4",
"value": "1"
},
]
};
I was able to access the value using below code. How can i return the entire JSON with updated value?
let variale = Immutable.fromJS(sampleJson).
getIn(['list']).
find(allocation => allocation.get("Bid") === "1").
getIn(['group']).
find(fun => fun.get("Cid") === "4").set('value',"FOUR");
Anyone has any suggestions on how to resolve this problem?
I think you can try to do this like so:
let immutable = Immutable.fromJS(sampleJson);
immutable = immutable.setIn(['list', 0, 'group', 0, 'value'], 'FOUR');
This monstrosity is how I would do it:
const newData = originalData.update('list', list => {
const itemIndex = list.findIndex(item => item.get('Bid') === '2');
return list.update(itemIndex, listItem => {
return listItem.update('group', groupList => {
const groupIndex = list.findIndex(group => group.get('Cid') === '4');
return groupList.update(groupIndex, group => {
return group.set('value', 'FOUR');
});
});
});
});
https://jsbin.com/latupo/7/edit?html,js,console
Personally I stopped using Immutable, I always found it a bit painful (not to mention those docs!). I now use redux and good old cloning to not mutate state. Less performant in theory but if you've got nothing that runs over a few milliseconds anyway, save yourself the trouble...

React Js create option list from array

I'm trying to create an options for my select list:
getMarkOptions: function () {
var options = this.props.renderProps;
return options.map(function (mark, i) {
return <option
key={i}
value={mark}>
{mark}
</option>
});
},
render: function () {
console.log('mark editor ' + this.props);
var selectedMark = this.props.value,
row = this.props.data,
highlightCurrentDay = this.props.highlight ? 'edit-select__currentDay':'';
return (
<select
value={selectedMark}
ref="selectMark"
className={"edit-select form-control " + highlightCurrentDay}
onChange={this.onChange}
>
{this.getMarkOptions()}
</select>
);
}
Data:
var marks = [
{"id": 1, "caption": "/", "code": "/", "meaning": "Present (AM)", "isStandardCode": true},
{"id": 2, "caption": "\\", "code": "\\", "meaning": "Present (PM)", "isStandardCode": true},
{"id": 3, "caption": "B", "code": "B", "meaning": "Educated off site", "isStandardCode": true},
{"id": 4, "caption": "C", "code": "C", "meaning": "Other Authorised Circumstances", "isStandardCode": true},
{"id": 5, "caption": "D", "code": "D", "meaning": "Dual registration", "isStandardCode": true}
];
I keep getting the error:
Unhandled rejection Invariant Violation: Objects are not valid as a React child (found: object with keys {id, caption, code, meaning, isStandardCode}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons.
Can anyone help?
The invariant is pointing out that the child to the option tag is an object - <option>{mark}</option> - but should be a valid child e.g. string, int, React Component, etc - <option>{mark.meaning}</option>
Try something like this:
return options.map(function (mark, i) {
return <option
key={mark.id}
value={mark.code}>
{mark.meaning}
</option>
});

Issue with the object references in Angularjs

I have a simple function below using which am trying create dynamic div containing a d3 chart for each row from the given input "json.res_full_sk"
However, when I use apply in Angularjs, am losing the historic data that is being assigned to dyndata object.
<script>
var app = angular.module('rpPlotExampleApp', ['ui.rpplot']);
app.controller('rpPlotCtrl', function ($scope) {
$scope.dyndata={};
$scope.assign_sk = function (testjsobj) {
alert('From Angular JS'+ JSON.stringify(testjsobj));
$scope.dyndata=testjsobj;
};
});
</script>
<script type="text/javascript">
jsonList = [];
jsonList.res_full_sk = [
{ "tid": 20, "sk": [{ "name": "Banking", "value": 40, "id": 0 }, { "name": "Housing", "value": 4, "id": 1 }, { "name": "Home", "value": 4, "id": 2 }, { "name": "NET", "value": 4, "id": 3 }] },
{ "tid": 22, "sk": [{ "name": "Movie", "value": 12, "id": 0 }, { "name": "Movie2", "value": 4, "id": 1 }, { "name": "Housing", "value": 4, "id": 2 }, { "name": "Banking", "value": 4, "id": 3 }, { "name": "C", "value": 4, "id": 4 }] },
{ "tid": 24, "sk": [{ "name": "Housing", "value": 4, "id": 0 }, { "name": "Home", "value": 4, "id": 1 }, { "name": "Banking", "value": 4, "id": 2 }] }
];
arr = [];
function getObjContents(i) {
arr = $.grep(jsonList.res_full_sk, function (e) {
return e.tid == i;
});
var str = "";
for (var i = 0; i < arr[0].sk.length; i++) {
str += ","+ JSON.stringify(arr[0].sk[i]);
}
str=str.substring(1);
str = "[" + str + "]";
str_obj=JSON.parse(str);
str_fin = {};
str_obj.forEach(function (e, i) {
str_fin['d' + i] = e;
});
return str_fin;
}
function insertDiv(Num) {
array = [];
var ar1 = Num.replace('[', '');
array = JSON.parse("[" + ar1 + "]");
var i = 0;
for (i; i < array.length; i++) {
js_data={};
js_data=getObjContents(array[i]);
testjsobj=js_data;
var scope = angular.element(document.getElementById("rpPlotCtrl")).scope();
scope.$watch("dyndata", function (oldval, newval) {
}, true);
scope.$apply(function () {
scope.dyndata = js_data;
});
var id_num;
id_num = array[i];
var rChart = angular.element(document.getElementById("sktemp"));
rChart.injector().invoke(function ($compile) {
$('<div obj></div>').insertAfter("#sktemp")
var obj = $('[obj]'); // get wrapper
var scope1 = $('[obj]').scope();
// generate dynamic content
alert(JSON.stringify(scope1.dyndata));
obj.html("<div class='skpro' id='skpro" + id_num + "' name='cn'><div class='skprovis' id='skprovis" + id_num + "' >Hello " + id_num + "<div class='half-plot' style='height:95%;width:95%;margin-top:0px;margin-left:5px;'><rp-plot dsn='dyndata' point-radius='1.5' scale='log' labelled='true' class='plot-rp-plot'></rp-plot></div></div></div>");
// compile!!!
console.log(obj.contents());
$compile(obj.contents())(scope1);
scope1.$apply();
});
}
}
</script>
The problem is dyndata is being changed for each value of i in the for loop, and thus the d3 chart is updated with the lastly assigned dyndata object.
As I see the dyndata object is being assigned with different values each time for loop executes, but it is not remaining but changes with the next value of dyndata to all the charts that were created.
How can I get the historic data to persist for dyndata that is already rendered for each for loop execution?
Am new to Angularjs and not very sure how I can make use of Angular.copy() for this scenario.
Problem seems to be with below code -
var scope = angular.element(document.getElementById("rpPlotCtrl")).scope();
scope.$watch("dyndata", function (oldval, newval) {
}, true);
scope.$apply(function () {
scope.dyndata = js_data;
});
as soon as I assign new js_data value to dyndata, all the existing charts gets updated with new value.
I tried using angular.copy(scope.dyndata)=js_data.. But it didn't work out.
Can you please suggest me?

Underscore JS to retrieve object by ID

I have a input where I want to find an object by ID. At the moment I am returning both objects but what I want is if I search '01' I would just return the first object. I have tried underscore _.map to do this but it did not give the result I am after.
var getById = function() {
var deferred = Q.defer(),
result;
result = items;
if (!result) {
deferred.reject('item not found');
} else {
deferred.resolve(result);
}
return deferred.promise;
};
JSON:
[{
"id": "01",
"name": "test1",
"orderItems": [
{
"productNumber": "TESTa",
"quantity": 2,
},
{
"productNumber": "TESTb",
"quantity": 4,
},
{
"productNumber": "TESTc",
"quantity": 6,
}
]
},{
"id": "02",
"name": "test2",
"orderItems": [
{
"productNumber": "TESTe",
"quantity": 2,
},
{
"productNumber": "TESTf",
"quantity": 7,
},
{
"productNumber": "TESTg",
"quantity": 6,
}
]
}]
You can use _.filter()
Looks through each value in the list, returning an array of all the values that pass a truth test (predicate).
result = _.filter(items, function(item){
return item.id == '01';
});
Or, _.findWhere
Looks through the list and returns the first value that matches all of the key-value pairs listed in properties.
result = _.findWhere(items, {id : '01'});
var result = _.find(myItems, function(item) { return item.id === '01'; }
If you find single item which matches the conditions, use _.find()
It looks through each value in the list, returning the first one that
passes a truth test
var _exist = _.find(_items, function (item) {
return item.id == your_id;
});
If you find all items which matches the conditions, use _.filter()
It looks through each value in the list, returning an array of all the
values that pass a truth test
var _exist = _.filter(_items, function (item) {
return item.id == your_id;
});
Catch the complete documentation here:
http://underscorejs.org/

Updating nested array inside array mongodb [duplicate]

This question already has answers here:
Updating a Nested Array with MongoDB
(2 answers)
Closed 3 years ago.
I have the following mongodb document structure:
[
{
"_id": "04",
"name": "test service 4",
"id": "04",
"version": "0.0.1",
"title": "testing",
"description": "test",
"protocol": "test",
"operations": [
{
"_id": "99",
"oName": "test op 52222222222",
"sid": "04",
"name": "test op 52222222222",
"oid": "99",
"description": "testing",
"returntype": "test",
"parameters": [
{
"oName": "Param1",
"name": "Param1",
"pid": "011",
"type": "582",
"description": "testing",
"value": ""
},
{
"oName": "Param2",
"name": "Param2",
"pid": "012",
"type": "58222",
"description": "testing",
"value": ""
}
]
}
]
}
]
I have been able to use $elemMatch in order to update fields in operations, but when I try to do the same thing (modified) for parameters it does not seem to work. I was wondering what other approach should I look into trying in order to be able to successfully update fields in a specific parameter, looking it up by its pid.
The update code I currently have and does not work looks like this:
var oid = req.params.operations;
var pid = req.params.parameters;
collection.update({"parameters":{"$elemMatch": {"pid": pid}}},{"$set": {"parameters.$.name":req.body.name, "parameters.$.description": req.body.description,"parameters.$.oName": req.body.oName,"parameters.$.type": req.body.type} }, function(err, result) {
if (err) {
console.log('Error updating service: ' + err);
res.send({'error':'An error has occurred'});
} else {
// console.log('' + result + ' document(s) updated');
res.send(result);
}
});
MongoDB 3.6 and newer
With MongoDB 3.6 and above comes a new feature that allows you to update nested arrays by using the positional filtered $\[<identifier>\] syntax in order to match the specific elements and apply different conditions through arrayFilters in the update statement:
const { oid, pid } = req.params;
const { name, oName, description, type } = req.body;
collection.update(
{
"_id": 1,
"operations": {
"$elemMatch": {
oid, "parameters.pid": pid
}
}
},
{ "$set": {
"operations.$[outer].parameters.$[inner].name": name,
"operations.$[outer].parameters.$[inner].description": description,
"operations.$[outer].parameters.$[inner].oName": oName,
"operations.$[outer].parameters.$[inner].type": type
} },
{ "arrayFilters": [
{ "outer.oid": oid },
{ "inner.pid": pid }
] }, (err, result) => {
if (err) {
console.log('Error updating service: ' + err);
res.send({'error':'An error has occurred'});
} else {
// console.log('' + result + ' document(s) updated');
res.send(result);
}
});
For MongoDB 3.4 and older:
As #wdberkeley mentioned in his comment:
MongoDB doesn't support matching into more than one level of an array.
Consider altering your document model so each document represents an
operation, with information common to a set of operations duplicated
in the operation documents.
I concur with the above and would recommend redesigning your schema as MongoDB engine does not support multiple positional operators ( See Multiple use of the positional $ operator to update nested arrays)
However, if you know the index of the operations array that has the parameters object to be updated beforehand then the update query will be:
db.collection.update(
{
"_id" : "04",
"operations.parameters.pid": "011"
},
{
"$set": {
"operations.0.parameters.$.name": "foo",
"operations.0.parameters.$.description": "bar",
"operations.0.parameters.$.type": "foo"
}
}
)
EDIT:
If you would like to create the $set conditions on the fly i.e. something which would help you get the indexes for the objects and then modify accordingly, then consider using MapReduce.
Currently this seems to be not possible using the aggregation framework. There is an unresolved open JIRA issue linked to it. However, a workaround is possible with MapReduce. The basic idea with MapReduce is that it uses JavaScript as its query language but this tends to be fairly slower than the aggregation framework and should not be used for real-time data analysis.
In your MapReduce operation, you need to define a couple of steps i.e. the mapping step (which maps an operation into every document in the collection, and the operation can either do nothing or emit some object with keys and projected values) and reducing step (which takes the list of emitted values and reduces it to a single element).
For the map step, you ideally would want to get for every document in the collection, the index for each operations array field and another key that contains the $set keys.
Your reduce step would be a function (which does nothing) simply defined as var reduce = function() {};
The final step in your MapReduce operation will then create a separate collection operations that contains the emitted operations array object along with a field with the $set conditions. This collection can be updated periodically when you run the MapReduce operation on the original collection.
Altogether, this MapReduce method would look like:
var map = function(){
for(var i = 0; i < this.operations.length; i++){
emit(
{
"_id": this._id,
"index": i
},
{
"index": i,
"operations": this.operations[i],
"update": {
"name": "operations." + i.toString() + ".parameters.$.name",
"description": "operations." + i.toString() + ".parameters.$.description",
"type": "operations." + i.toString() + ".parameters.$.type"
}
}
);
}
};
var reduce = function(){};
db.collection.mapReduce(
map,
reduce,
{
"out": {
"replace": "operations"
}
}
);
Querying the output collection operations from the MapReduce operation will typically give you the result:
db.operations.findOne()
Output:
{
"_id" : {
"_id" : "03",
"index" : 0
},
"value" : {
"index" : 0,
"operations" : {
"_id" : "96",
"oName" : "test op 52222222222",
"sid" : "04",
"name" : "test op 52222222222",
"oid" : "99",
"description" : "testing",
"returntype" : "test",
"parameters" : [
{
"oName" : "Param1",
"name" : "foo",
"pid" : "011",
"type" : "foo",
"description" : "bar",
"value" : ""
},
{
"oName" : "Param2",
"name" : "Param2",
"pid" : "012",
"type" : "58222",
"description" : "testing",
"value" : ""
}
]
},
"update" : {
"name" : "operations.0.parameters.$.name",
"description" : "operations.0.parameters.$.description",
"type" : "operations.0.parameters.$.type"
}
}
}
You can then use the cursor from the db.operations.find() method to iterate over and update your collection accordingly:
var oid = req.params.operations;
var pid = req.params.parameters;
var cur = db.operations.find({"_id._id": oid, "value.operations.parameters.pid": pid });
// Iterate through results and update using the update query object set dynamically by using the array-index syntax.
while (cur.hasNext()) {
var doc = cur.next();
var update = { "$set": {} };
// set the update query object
update["$set"][doc.value.update.name] = req.body.name;
update["$set"][doc.value.update.description] = req.body.description;
update["$set"][doc.value.update.type] = req.body.type;
db.collection.update(
{
"_id" : oid,
"operations.parameters.pid": pid
},
update
);
};
If it is data that is changed frequently, you should flatten the structure and separate the data that changes a lot from that which does not.
If it is data that does not change often, and the entire data object is not massive, just modify the object client-side, and update the entire object.
We will try to find index of outer array(i) and inner array(j)and then update
collection.findById(04)
.then(result =>{
for(let i = 0; i<result.operations.length; i++){
if(result.operation[i]._id == "99"){
let parameters = result.operations[i].parameters;`enter code here`
for(let j = 0; j<parameters.length; j++){
if(parameters[j].pid == "011"){
console.log("i", i);
console.log("j", j);
let data = {}
data["operations." + i + ".parameters." + j + ".oName"] = updateoName
data["operations." + i + ".parameters." + j + ".name"] = updatename
data["operations." + i + ".parameters." + j + ".pid"] = updatepid
data["operations." + i + ".parameters." + j + ".description"] = updatedescription
data["operations." + i + ".parameters." + j + ".value"] = updatevalue
console.log(data)
collection.update({
"_id": "04"
},{
$set: data
})
.then(dbModel => res.json(dbModel))
}
}
}
}
})
Starting with mongo version 3.6 you can use the $[] in Conjunction with $[] to Update Nested Arrays
Update Nested Arrays in Conjunction with $[]
The $[] filtered positional operator, in conjunction with
all $[] positional operator can be used to update nested arrays.
https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/#position-nested-arrays-filtered

Resources