Repository: guregu/dynamo

Issue:
Set if not nil
Hello,

In certain applications, there are cases where the value of a key should only be updated if the new value is not nil, i.e. 

```go
	if cookies != nil {
		u.Set("cookies", *cookies)
	}
	if country != nil {
		u.Set("country", *country)
	}
	if language != nil {
		u.Set("language", *language)
	}
```

Seems there are a few helper funcs already in `update.go`, some of which from what I've seen use Dynamo functions, but would it be possible to also include something like this ?

```go
// SetIfNotNil changes path to the given value, if the value is not nil.
func (u *Update) SetIfNotNil(path string, value interface{}) *Update {
	if value == nil {
		return u
	}
	
	return u.Set(path, value)
}
```

It would essentially open up the path to do:

```
u.SetIfNotNil("cookies", cookies)
  .SetIfNotNil("country", country)
  ...
```


I can make a PR if this is something that is likely to be accepted into the lib.

Thanks! 

Hints from issue comments:
@guregu: Given that nil values have special handling in Set, I think this makes sense. If you make a PR I'll merge it.
Just make sure you do the nil check after you marshal the value because custom types etc. can marshal into nil to get omitted: https://github.com/guregu/dynamo/blob/ea7f33244d17a2b0e5057358981be9920da23cf3/update.go#L75-L80@adrianlungu: Added nil check for both to skip marshalling if `value` is nil. @adrianlungu: @guregu what do you think about having something like `SetStruct` as well ? 

I've been looking at how `Update` and `Put` work, and it looks like `Update` could potentially support a `SetStruct` function that would be able to take the `hash` from the `tag`, and populate the various `set, add, del, remove` based on the values and tags of the struct being given. 

That would mean that, instead of having to do a lot of `Set(...).Set(...).Set(...)` one could just do `SetStruct(s)`. @adrianlungu: Closed by #251 

Edit: Thanks @guregu ! 🙌 

Solution patch:
diff --git a/update.go b/update.go
index 17757a6..4477cf7 100644
--- a/update.go
+++ b/update.go
@@ -132,6 +132,26 @@ func (u *Update) SetIfNotExists(path string, value interface{}) *Update {
 	return u
 }
 
+// SetIgnoringNil changes path to the given value only if the value and marshalled value are not nil.
+func (u *Update) SetIgnoringNil(path string, value interface{}) *Update {
+	if value == nil {
+		return u
+	}
+
+	v, err := marshal(value, flagNone)
+	if v == nil && err == nil {
+		return u
+	}
+	u.setError(err)
+
+	path, err = u.escape(path)
+	u.setError(err)
+	expr, err := u.subExpr("🝕 = ?", path, v)
+	u.setError(err)
+	u.set = append(u.set, expr)
+	return u
+}
+
 // SetExpr performs a custom set expression, substituting the args into expr as in filter expressions.
 // See: http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateItem.html#DDB-UpdateItem-request-UpdateExpression
 //



Test patch:
diff --git a/update_test.go b/update_test.go
index e2f1f15..f204fcf 100644
--- a/update_test.go
+++ b/update_test.go
@@ -80,6 +80,7 @@ func TestUpdate(t *testing.T) {
 	err = table.Update("UserID", item.UserID).
 		Range("Time", item.Time).
 		Set("Msg", "changed").
+		SetIgnoringNil("Msg", nil).
 		SetExpr("Meta.$ = ?", "foo", "baz").
 		SetExpr("$", setLit).
 		Add("Count", 1).