var data = List();
snapshot.data.documents.forEach((doc) => data.add(doc.data)); //my example data is one doc
data.add({"x": "y"}); //works
data[0]["bar"].add({"foo": "bar"}); //doesn't
My data looks like this:
data = [
{
"foo": "",
"bar": [
{"foo": "bar"},
{"foo": "bar"},
{"foo": "bar"},
],
},
];
When I do the second array modification, it gives me this error:
Unhandled Exception: Unsupported operation: Cannot add to a fixed-length list
I have tried doing this with normal data that is not fetched from firebase, and it works as expected. I also tried using List.from to populate the array but it didn't work either.
snapshot.data.documents[0]['bar'] apparently is a fixed-length (i.e., non-growable) List. You can't make a non-growable List growable. You instead need to create a growable copy:
data[0]["bar"] = data[0]["bar"].toList(); // Create a copy.
data[0]["bar"].add({"foo": "bar"}); // Now this should work.
Alternatively:
data[0]["bar"] = [...data[0]["bar"], {"foo": "bar"}];
However, while the latter approach is more compact, it is less clear that the copy is explicitly desired.
Do like this:
var data = List();
var data2= List();
snapshot.data.documents.forEach((doc) => data.add(doc.data)); //my example data is one doc
data.add({"x": "y"}); //works
data2=data[0]["bar"];
data2.add({"foo": "bar"});
The problem is add method is used over an object type. You need to add a cast before using add method:
(data[0]["bar"] as List<Map<String, String>>).add({"foo": "bar"});
Related
I am struggling to find the correct syntax to search a String Set/List target attribute (tags) by an array of strings. The idea would be that if the SS attribute contains all of the passed strings, it passes the filter. The passed strings do not need to match all of the strings within the target attribute. The more strings you pass, the more accurate your results.
// Compile tags into a list
let tagSqlValues = {};
let tagSqlStatement = query.tags.map((tag: string, index: number) => {
let tagParam = `:tag${index}`;
tagSqlValues[tagParam] = tag;
return `${tagParam} in tags`;
}).join(" and ");
// Console Logs
// tagSqlStatement = :tag0 in tags and :tag1 in tags (also tried tags contains :tag0 and tags contains :tag1)
// tagSqlValues = {":tag0":"Modern",":tag1":" Spring"}
let params = {
TableName: "Art",
FilterExpression: tagSqlStatement,
ExpressionAttributeValues: tagSqlValues,
};
let results = await this.DDB_CLIENT.scan(params).promise();
// Console Logs
// "Invalid FilterExpression: Syntax error; token: \"tags\", near: \"in tags and\""
// "Invalid FilterExpression: Syntax error; token: \"contains\", near: \"tags contains :tag0\""
I've tried several variations with IN and CONTAINS without luck. Is this possible with DynamoDB?
It looks like my CONTAINS syntax was wrong. I did a little digging and found this answer by Zanon. With a minor modification to include the and join, it seems like the filter is working as expected!
// Compile tags into a list
let tagSqlValues = {};
let tagSqlStatement = query.tags.map((tag: string, index: number) => {
let tagParam = `:tag${index}`;
tagSqlValues[tagParam] = tag;
return `contains(tags, ${tagParam})`;
}).join(" and ");
I've got state with a nested array that looks like the following:
{
list: [
{
id: '3546f44b-457e-4f87-95f6-c6717830294b',
title: 'First Nest',
key: '0',
children: [
{
id: '71f034ea-478b-4f33-9dad-3685dab09171',
title: 'Second Nest',
key: '0-0
children: [
{
id: '11d338c6-f222-4701-98d0-3e3572009d8f',
title: 'Q. Third Nest',
key: '0-0-0',
}
],
}
],
],
selectedItemKey: '0'
}
Where the goal of the nested array is to mimic a tree and the selectedItemKey/key is how to access the tree node quickly.
I wrote code to update the title of a nested item with the following logic:
let list = [...state.list];
let keyArr = state.selectedItemKey.split('-');
let idx = keyArr.shift();
let currItemArr = list;
while (keyArr.length > 0) {
currItemArr = currItemArr[idx].children;
idx = keyArr.shift();
}
currItemArr[idx] = {
...currItemArr[idx],
title: action.payload
};
return {
...state,
list
};
Things work properly for the first nested item, but for the second and third level nesting, I get the following Immer console errors
An immer producer returned a new value *and* modified its draft.
Either return a new value *or* modify the draft.
I feel like I'm messing up something pretty big here in regards to my nested array access/update logic, or in the way I'm trying to make a new copy of the state.list and modifying that. Please note the nested level is dynamic, and I do not know the depth of it prior to modifying it.
Thanks again in advance!
Immer allows you to modify the existing draft state OR return a new state, but not both at once.
It looks like you are trying to return a new state, which is ok so long as there is no mutation. However you make a modification when you assign currItemArr[idx] = . This is a mutation because the elements of list and currItemArr are the same elements as in state.list. It is a "shallow copy".
But you don't need to worry about shallow copies and mutations because the easier approach is to just modify the draft state and not return anything.
You just need to find the correct object and set its title property. I came up with a shorter way to do that using array.reduce().
const keyArr = state.selectedItemKey.split("-");
const target = keyArr.reduce(
(accumulator, idx) => accumulator.children[idx],
{ children: state.list }
);
target.title = action.payload;
I am trying to append to a string set (array of strings) column, which may or may not already exist, in a DynamoDB table. I referred to SO questions like this and this when writing my UpdateExpression.
My code looks like this.
const AWS = require('aws-sdk')
const dynamo = new AWS.DynamoDB.DocumentClient()
const updateParams = {
// The table definitely exists.
TableName: process.env.DYNAMO_TABLE_NAME,
Key: {
email: user.email
},
// The column may or may not exist, which is why I am combining list_append with if_not_exists.
UpdateExpression: 'SET #column = list_append(if_not_exists(#column, :empty_list), :vals)',
ExpressionAttributeNames: {
'#column': 'items'
},
ExpressionAttributeValues: {
':vals': ['test', 'test2'],
':empty_list': []
},
ReturnValues: 'UPDATED_NEW'
}
dynamo.update(updateParams).promise().catch((error) => {
console.log(`Error: ${error}`)
})
However, I am getting this error: ValidationException: An operand in the update expression has an incorrect data type. What am I doing incorrectly here?
[Update]
Thanks to Nadav Har'El's answer, I was able to make it work by amending the params to use the ADD operation instead of SET.
const updateParams = {
TableName: process.env.DYNAMO_TABLE_NAME,
Key: {
email: user.email
},
UpdateExpression: 'ADD items :vals',
ExpressionAttributeValues: {
':vals': dynamo.createSet(['test', 'test2'])
}
}
A list and a string set are not the same type - a string set can only hold strings while a list may hold any types (including nested lists and objects), element types don't need to be the same, and a list can hold also duplicate items. So if your original item is indeed as you said a string set, not a list, this explains why this operation cannot work.
To add items to a string set, use the ADD operation, not the SET operation. The parameter you will give to add should be a set (not a list, I don't know the magic js syntax to specify this, check your docs) with a bunch of elements. If the attribute already exists these elements will be added to it (dropping duplicates), and if the attribute doesn't already exit, it will be set to the set of these elements. See the documentation here: https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateItem.html#DDB-UpdateItem-request-UpdateExpression
I'm working with Fullcalendar and I'm trying to get resources as function
resources: function(callback){
var manageEvent = new ManageEvent();
var request = manageEvent.getEmployees();
request.always(function (param) {
//location.reload();
var list = [];
var emp;
for (var elem in param) {
emp = param[elem];
list.push({
'id': emp['cp_collaboratore'],
'title': emp['cognome_col']
});
}
var t = JSON.stringify(list);
callback(t);
});
request.catch(function (param) {
alert('errore');
});
},
I checked the variable 't' through log and it shows the following result:
[{"id":"1","title":"name_1"},{"id":"2","title":"name_2"},{"id":"3","title":"name_3"},{"id":"5","title":"name_4"},{"id":"9","title":"name_5"}]
but it don't works and shows the following error message:
Uncaught TypeError: resourceInputs.map is not a function
at ResourceManager.setResources
You just need to write
callback(list);
t in your code is a string, because you converted your list array into a string using JSON.stringify(). But fullCalendar expects an actual array, not a string. It can't run functions or read individual properties from a string.
You can remove the line var t = JSON.stringify(list); completely, it's not needed.
Generally the only reason you'd use stringify() is if you wanted to log the value to your console for debugging, or convert the array into JSON if you wanted to send it somewhere else using AJAX. It makes no sense to pass arrays and objects around inside JavaScript as serialised strings, when you can just use the objects themselves.
I use Dapper with dynamics because the table to be queries is not known until runtime, so POCO classes aren't possible.
I am returning Dapper's results via WebAPI. To save bandwidth, I need to return just the values from Dapper, not the property names, e.g.:
{
7,"2013-10-01T00:00:00",0,"AC",null,"ABC","SOMESTAGE"
},...
And not:
{
TID: 7,
CHANGE_DT: "2013-10-01T00:00:00",
EFFECTIVE_APPTRANS: 0,
EFFECTIVE_APPTRANS_STATUS: "AC",
DEVICE: null,
PROCESS: "ABC",
STAGE: "SOMESTAGE"
},...
I'm having some trouble figuring out a reasonable way to do this. I've tried abusing Dapper's mapping feature:
var tableData = Database.Query<dynamic, dynamic, dynamic>(connectInfo, someResource.sql,
(x, y) =>
{
List<object> l = new List<object>();
object o = x;
foreach(var propertyName in o.GetType().GetProperties().Select(p => p.Name)) {
object value = o.GetType().GetProperty(propertyName).GetValue(o, null);
l.Add(value);
}
return l as dynamic;
},
new {implantId, pendingApptrans},
splitOn: "the_last_column");
I've also tried having Dapper return a List using the same base code.
The idea was that I'd extract property values within the map function because it allows me to play with rows before anything is returned, but I get empty results without error:
[
[ ], [ ], [ ], [ ], [ ], [ ], [ ]
]
Additionally, I don't know any column names to split on, which the mapping feature wants. However even when I enter the last column name from a test query, the results are the same.
How can I return only values from Dapper's dynamic return value?
Do I need to resort to post-processing the dynamic after the Dapper call?
For that particular set of requirements, yes you would need to post-process; for example, you could cast to DapperRow or IDictionary<string,object>. Alternatively, you could use the IDataReader API that dapper now exposes.