From 84138864d0f593e23c76deb3b9da430cbb0a4ab2 Mon Sep 17 00:00:00 2001 From: Kevin Hurley <kph@fb.com> Date: Fri, 11 Sep 2015 10:25:14 -0700 Subject: [PATCH] Add ability to merge dynamic objects 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 | 35 +++++++++++++++++++++++++++++++++++ folly/dynamic.h | 17 +++++++++++++++++ folly/test/DynamicTest.cpp | 21 +++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/folly/dynamic-inl.h b/folly/dynamic-inl.h index 6e2fd62a..ff503c4a 100644 --- a/folly/dynamic-inl.h +++ b/folly/dynamic-inl.h @@ -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); diff --git a/folly/dynamic.h b/folly/dynamic.h index bea7fc44..fa494555 100644 --- a/folly/dynamic.h +++ b/folly/dynamic.h @@ -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. * diff --git a/folly/test/DynamicTest.cpp b/folly/test/DynamicTest.cpp index 7ba744fb..97af70be 100644 --- a/folly/test/DynamicTest.cpp +++ b/folly/test/DynamicTest.cpp @@ -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) { -- 2.34.1