Add ability to merge dynamic objects
authorKevin Hurley <kph@fb.com>
Fri, 11 Sep 2015 17:25:14 +0000 (10:25 -0700)
committerfacebook-github-bot-9 <folly-bot@fb.com>
Fri, 11 Sep 2015 18:20:19 +0000 (11:20 -0700)
Summary: Adding ability to merge dynamic objects with another object.  It will just overwrite duplicate keys

Reviewed By: @yfeldblum

Differential Revision: D2413628

folly/dynamic-inl.h
folly/dynamic.h
folly/test/DynamicTest.cpp

index 6e2fd62a290705d5d0e7f7902e2512a9e1bf8005..ff503c4a0d7350d18add9a3fbd8509383de883c5 100644 (file)
@@ -512,6 +512,41 @@ template<class K, class V> inline void dynamic::insert(K&& key, V&& val) {
   rv.first->second = std::forward<V>(val);
 }
 
+inline void dynamic::update(const dynamic& mergeObj) {
+  if (!isObject() || !mergeObj.isObject()) {
+    throw TypeError("object", type(), mergeObj.type());
+  }
+
+  for (const auto& pair : mergeObj.items()) {
+    (*this)[pair.first] = pair.second;
+  }
+}
+
+inline void dynamic::update_missing(const dynamic& mergeObj1) {
+  if (!isObject() || !mergeObj1.isObject()) {
+    throw TypeError("object", type(), mergeObj1.type());
+  }
+
+  // Only add if not already there
+  for (const auto& pair : mergeObj1.items()) {
+    if ((*this).find(pair.first) == (*this).items().end()) {
+      (*this)[pair.first] = pair.second;
+    }
+  }
+}
+
+inline dynamic dynamic::merge(
+    const dynamic& mergeObj1,
+    const dynamic& mergeObj2) {
+
+  // No checks on type needed here because they are done in update_missing
+  // Note that we do update_missing here instead of update() because
+  // it will prevent the extra writes that would occur with update()
+  auto ret = mergeObj2;
+  ret.update_missing(mergeObj1);
+  return ret;
+}
+
 inline std::size_t dynamic::erase(dynamic const& key) {
   auto& obj = get<ObjectImpl>();
   return obj.erase(key);
index bea7fc442e9fa9f1182eb5219fc1640904b2e0ed..fa494555ba2174108cdf1597b7e84fdd35c8185b 100644 (file)
@@ -436,6 +436,23 @@ public:
    */
   template<class K, class V> void insert(K&&, V&& val);
 
+  /*
+   * These functions merge two folly dynamic objects.
+   * The "update" and "update_missing" functions extend the object by
+   *  inserting the key/value pairs of mergeObj into the current object.
+   *  For update, if key is duplicated between the two objects, it
+   *  will overwrite with the value of the object being inserted (mergeObj).
+   *  For "update_missing", it will prefer the value in the original object
+   *
+   * The "merge" function creates a new object consisting of the key/value
+   * pairs of both mergeObj1 and mergeObj2
+   * If the key is duplicated between the two objects,
+   *  it will prefer value in the second object (mergeObj2)
+   */
+  void update(const dynamic& mergeObj);
+  void update_missing(const dynamic& other);
+  static dynamic merge(const dynamic& mergeObj1, const dynamic& mergeObj2);
+
   /*
    * Erase an element from a dynamic object, by key.
    *
index 7ba744fb3a55351721a9c44ff9e88cc322326895..97af70be462e6df6b3f7f5561e48191f1b323cb3 100644 (file)
@@ -98,6 +98,27 @@ TEST(Dynamic, ObjectBasics) {
 
   // We don't allow objects as keys in objects.
   EXPECT_ANY_THROW(newObject[d3] = 12);
+
+  // Merge two objects
+  dynamic origMergeObj1 = folly::dynamic::object();
+  dynamic mergeObj1 = origMergeObj1 = folly::dynamic::object
+    ("key1", "value1")
+    ("key2", "value2");
+  dynamic mergeObj2 = folly::dynamic::object
+    ("key2", "value3")
+    ("key3", "value4");
+  dynamic combinedObj = folly::dynamic::object
+    ("key1", "value1")
+    ("key2", "value3")
+    ("key3", "value4");
+  auto newMergeObj = dynamic::merge(mergeObj1, mergeObj2);
+  EXPECT_EQ(newMergeObj, combinedObj);
+  EXPECT_EQ(mergeObj1, origMergeObj1); // mergeObj1 should be unchanged
+
+  mergeObj1.update(mergeObj2);
+  EXPECT_EQ(mergeObj1, combinedObj);
+  dynamic arr = { 1, 2, 3, 4, 5, 6 };
+  EXPECT_THROW(mergeObj1.update(arr), std::exception);
 }
 
 TEST(Dynamic, ObjectErase) {