Suppose I have a List<map> that looks like this:
‘Books’:[
‘B1’{
‘id’: ’1234’,
‘bookName’: ’book1’
},
‘B2’{
‘id’: ’4567’,
‘bookName’: ’book2’
},
‘B3’{
‘id’: ’1234’,
‘bookName’: ’book3’
},
‘B4’{
‘id’: ’8912’,
‘bookName’: ’book4’
},
…
];
I’m trying to return the entire book without duplications in Id.
The expected result should be like this:
‘B1’{
‘id’: ’1234’,
‘bookName’: ’book1’
},
‘B2’{
‘id’: ’4567’,
‘bookName’: ’book2’
},
‘B4’{
‘id’: ’8912’,
‘bookName’: ’book4’
},
I guessed what your input map was and made a solution based on this answer from Basic Coder.
final list = {
'Books':[
{
'id':'1234',
'bookName':'book1'
},
{
'id':'4567',
'bookName':'book2'
},
{
'id': '1234',
'bookName':'book3'
},
{
'id': '8912',
'bookName':'book4'
},
]};
void main() {
print('With duplicates $list');
final ids = list['Books']!.map<String>((e) => e['id']!).toSet();
list['Books']!.retainWhere((Map x) {
return ids.remove(x['id']);
});
print('Without duplicates $list');
}
This code shows your input as the variable list, which seems to be what you were going for with your provided data. The code then obtains a list of each id of the book and removes duplicates by changing it to a Set. Then it only retains elements in the original list with those non-duplicate ids.
Remove the ! operators if you're not using null-safety.
Here's one inspired by Christopher's answer:
void main() {
var books = {
'B1': {
'id': '1234',
'bookName': 'book1',
},
'B2': {
'id': '4567',
'bookName': 'book2',
},
'B3': {'id': '1234', 'bookName': 'book3'},
'B4': {'id': '8912', 'bookName': 'book4'},
};
var seen = <String>{};
var kept = books.entries.where((me) => seen.add(me.value['id'] ?? 'OTHER'));
print(Map.fromEntries(kept));
}
I think it's a bit simpler, since it doesn't have to populate the Set first. I also learned that Set.add returns a bool to indicate the element didn't exist before. Nice.
Related
Is there a way of making transactWriteItem return the document it updated?
const transactionParams = {
ReturnConsumedCapacity: "INDEXES",
TransactItems: [
{
Delete: {
TableName: reactionTableName,
Key: {
"SOME_PK_",
"SOME_SK_",
},
ReturnValues: 'ALL_OLD',
},
},
{
Update: {
TableName: reviewTableName,
Key: { PK: "SOME_PK", SK: "SOME_SK" },
ReturnValues: 'ALL_OLD',
},
},
],
};
try {
const result = await docClient.transactWrite(transactionParams).promise();
} catch (error) {
context.done(error, null);
}
For example in the above code get the documents that were touched (before or after update)?
No, TransactWriteItems API does not provide the ability to return values of a modified item, however, you could obtain those values using DynamoDB Streams, otherwise you would need to default to the singleton APIs UpdateItem/DeleteItem which are not ACID compliant together.
So this should be simple...
I want to append a string to a StringSet in a DynamoDB if it exists, or create the StringSet property if it doesn't and set the value. If we could initialize the StringSet on creation with an empty array, it would be fine, but alas we can not.
Here's what I have so far:
const companiesTable = 'companies';
dynamodb.updateItem({
TableName: companiesTable,
Key: {
id: {
S: company.id
}
},
UpdateExpression: 'ADD socialAccounts = list_append(socialAccount, :socialAccountId)',
ExpressionAttributeValues: {
':socialAccountId': {
'S': [socialAccountId]
}
},
ReturnValues: "ALL_NEW"
}, function(err, companyData) {
if (err) return cb({ error: true, message: err });
const response = {
error: false,
message: 'Social account created',
socialAccountData
};
cb(response);
});
I've also tried...
UpdateExpression: 'SET socialAccounts = list_append(socialAccounts, :socialAccountId)',
ExpressionAttributeValues: {
':socialAccountId': {
S: socialAccountId
}
},
and...
UpdateExpression: 'ADD socialAccounts = :socialAccountId',
ExpressionAttributeValues: {
':socialAccountId': {
S: socialAccountId
}
},
and...
UpdateExpression: 'SET socialAccounts = [:socialAccountId]',
ExpressionAttributeValues: {
':socialAccountId': socialAccountId
},
and...
UpdateExpression: 'ADD socialAccounts = :socialAccountId',
ExpressionAttributeValues: {
':socialAccountId': socialAccountId
},
Among about every other variation of the above. Am I dumb? Is DynamoDB not capable of simple write/updates to an array type field? Do I REALLY have to lookup the item first to see if it has that field before I try to either add or set that field, because I can't instantiate that field with an empty array?
The ADD action handles the create/update logic, but only supports numbers and sets. You are trying to add a string type 'S'. You need to wrap this string in an array and pass it as a string set 'SS'. You also don't need the equals sign.
Your UpdateExpression and ExpressionAttributeValues should look like this:
UpdateExpression: 'ADD socialAccounts :socialAccountId',
ExpressionAttributeValues: {
':socialAccountId': {
'SS': [socialAccountId]
}
},
More information about updating items can be found here
My api basically returns something like this:
GET /api/projects/
{
"count": 26,
"next": "http://127.0.0.1:8000/api/projects/?page=2",
"previous": null,
"results": [
{
"id": 21,
"name": "Project A",
...
},
{
"id": 19,
"name": "Project B",
...
},
...
]
}
Using NgResource, I am able to query the api and get the data like this:
var PROJECT = $resource('/api/projects/:id/', {id:'#id'},{
query : {
method : 'GET',
isArray : false
}
});
factory.project_list = function(callback) {
PROJECT.query({},function(project_list){
factory.project_list = project_list.results;
callback();
});
};
My different projects are now available in factory.project_list. The issue here is that each item in factory.project_list are not ngResource items. So I can't call methods such as .$save(), .$update()...
I saw a transformResponse() function but I'm not able to get it working easily...
Do you have any idea what could be the best approach here ?
This is what worked for me:
app.config(['$resourceProvider', function($resourceProvider) {
$resourceProvider.defaults.stripTrailingSlashes = false;
}]);
services.factory('Project', ['$resource',
function($resource) {
return $resource('api/project/:id/', {}, {
query: {
method: 'GET',
url: 'api/projects/',
isArray: true,
transformResponse: function(data, headers) {
return angular.fromJson(data).results;
},
},
});
}
]);
It seems a no-brainer to me, but could not get this to work:
My ApplicationRoute:
model: function () {
this.controllerFor('categories').set('model', this.store.find('category'));
}
CategoriesController:
App.CategoriesController = Ember.ArrayController.extend();
ArticlesRoute: (using query-params-new)
model: function(params) {
if (params.category) {
return this.store.find('article').filter(function(item) {
console.log(item.get('category.id'); // => undefined
return (item.get('category.id') === params.category); // => always false
});
} else {
return this.store.find('article');
}
}
As you can see is the above code the problem. item.get('category.id') simple does always return undefined. However the Articles do have a category defined:
REST response: (including relationship values)
{
"articles":[
{
"id":116,
"name": "Article 1"
"category":[
11
],
},
{
"id":115,
"name": "Article 2"
"category":[
6
],
},
{
"id":114,
"name": "Article 3"
"category":[
11
],
}
],
"categories":[
{
"id":6,
"name":"Category 1",
},
{
"id":11,
"name":"Category 2",
}
],
}
Edit: item.get('category') does return a <DS.ManyArray:ember747> in the console.
I found it already.
HasMany is an Array of Objects. That way we have to use findBy to search for the ID. (correct me I am wrong).
Use item.get('category').findBy('id', params.category) to filter by ID.
I'm using Ember.js (v1.2.0) with an API which returns paginated JSON data like this:
{
"count": 5,
"next": "http://127.0.0.1:8000/some/resource/?page=2",
"previous": null,
"results": [
{
"id": 37,
"title": "Some title",
"description": "Some description",
},
{
"id": 35,
"title": "Sdflskdf",
"description": "sdfkdsjf",
},
{
"id": 34,
"title": "Some other title",
"description": "Dsdlfksdf",
},
]
}
I'm not using ember-data, so I'm using a plain ember object as my model and loading the data like this:
App.SomeResource = Ember.Object.extend({});
App.SomeResource.reopenClass({
find: function () {
return $.getJSON('/some/resource/').then(function (response) {
return response.results.map(function (data) {
return App.SomeResource.create(data);
});
});
},
});
The find method on my model class returns a promise which resolves to an array of objects. While creates SomeResource objects, all the pagination data is lost.
Is there a way to store count, next and previous page urls somewhere when the promise resolves?
I am assigning them to global object but you should do better.
App.SomeResource = Ember.Object.extend({});
App.SomeResource.reopenClass({
find: function () {
return $.getJSON('/some/resource/').then(function (response) {
return RSVP.all(response.results.map(function (data) {
return App.SomeResource.create(data);
})).then(function(createdResources) {
window.count = response.count;
window.next = response.next;
window.previous = response.previous;
return createdResources;
});
});
}
});
Rather than storing this metadata on the global window object, I came up with this:
App.SomeResource.reopenClass({
find: function () {
var url = '/some/resource/';
return Ember.$.getJSON(url).then(function (response) {
response.results = response.results.map(function (resource) {
return App.SomeResource.create(resource);
});
return response;
});
},
});
SomeResource.find() just instantiates Ember objects from the results array and then returns the response with the rest of the data untouched. The route then receives the response and sets up the pagination data and the model in the setupController function:
App.SomeResourceRoute = Ember.Route.extend({
model: function () {
return App.SomeResource.find();
},
setupController: function(controller, model) {
controller.setProperties({count: model.count,
pageCount: model.page_count,
currentPage: model.current_page,
pageRange: model.page_range,
previous: model.previous,
next: model.next,
model: model.results,});
},
});
It works, but maybe there is a better way.