Commit 41dde39b authored by Milo Yip's avatar Milo Yip

Merge pull request #96 from miloyip/issue91removeelement

Fix #91
parents 67143c2b b52e0782
......@@ -222,11 +222,26 @@ The correct length of the value `"a\u0000b"` is 3. But `strlen()` returns 1.
Besides, `std::string` also support a constructor:
~~~~~~~~~~cpp
string( const char* s, size_type count);
string(const char* s, size_t count);
~~~~~~~~~~
which accepts the length of string as parameter. This constructor supports storing null character within the string, and should also provide better performance.
## Comparing values
You can use `==` and `!=` to compare values. Two values are equal if and only if they are have same type and contents. You can also compare values with primitive types. Here is an example.
~~~~~~~~~~cpp
if (document["hello"] == document["n"]) /*...*/; // Compare values
if (document["hello"] == "world") /*...*/; // Compare value with literal string
if (document["i"] != 123) /*...*/; // Compare with integers
if (document["pi"] != 3.14) /*...*/; // Compare with double.
~~~~~~~~~~
Array and object must be `Value`s in order to be compared. They are equal if and only if their whole subtrees are equal.
Note that, currently if an object contains duplicated named member, comparing equality with any object is always `false`.
# Create/Modify Values {#CreateModifyValues}
There are several ways to create values. After a DOM tree is created and/or modified, it can be saved as JSON again using `Writer`.
......@@ -380,6 +395,8 @@ Value with array type provides similar APIs as `std::vector`.
* `Value& PushBack(Value&, Allocator&)`
* `template <typename T> GenericValue& PushBack(T, Allocator&)`
* `Value& PopBack()`
* `ValueIterator Erase(ConstValueIterator pos)`
* `ValueIterator Erase(ConstValueIterator first, ConstValueIterator last)`
Note that, `Reserve(...)` and `PushBack(...)` may allocate memory for the array elements, therefore require an allocator.
......@@ -411,12 +428,11 @@ contact.PushBack(val, document.GetAllocator());
~~~~~~~~~~
## Modify Object {#ModifyObject}
Object is a collection of key-value pairs. Each key must be a string value. The way to manipulating object is to add/remove members:
Object is a collection of key-value pairs. Each key must be a string value. The way to manipulating object is to add members:
* `Value& AddMember(Value&, Value&, Allocator& allocator)`
* `Value& AddMember(StringRefType, Value&, Allocator&)`
* `template <typename T> Value& AddMember(StringRefType, T value, Allocator&)`
* `bool RemoveMember(const Ch*)`
Here is an example.
......@@ -442,6 +458,16 @@ Value val(42); // some value
contact.AddMember(key, val, document.GetAllocator());
~~~~~~~~~~
For removing members, there are several choices:
* `bool RemoveMember(const Ch* name)`: Remove a member by search its name (linear time complexity).
* `bool RemoveMember(const Value& name)`: same as above but `name` is a Value.
* `MemberIterator RemoveMember(MemberIterator)`: Remove a member by iterator (_constant_ time complexity).
* `MemberIterator EraseMember(MemberIterator)`: similar to the above but it preserves order of members (linear time complexity).
* `MemberIterator EraseMember(MemberIterator first, MemberIterator last)`: remove a range of members, preserves order (linear time complexity).
`MemberIterator RemoveMember(MemberIterator)` uses a "move-last" trick to archive constant time complexity. Basically the member at iterator is destructed, and then the last element is moved to that position. So the order of the remaining members are changed.
## Deep Copy Value {#DeepCopyValue}
If we really need to copy a DOM tree, we can use two APIs for deep copy: constructor with allocator, and `CopyFrom()`.
......@@ -458,8 +484,8 @@ assert(v1.IsNull() && v2.IsNull()); // both moved to d
v2.CopyFrom(d, a); // copy whole document to v2
assert(d.IsArray() && d.Size() == 2); // d untouched
v1.SetObject().AddMember( "array", v2, a );
d.PushBack(v1,a);
v1.SetObject().AddMember("array", v2, a);
d.PushBack(v1, a);
~~~~~~~~~~
## Swap Values {#SwapValues}
......@@ -474,7 +500,7 @@ assert(a.IsString());
assert(b.IsInt());
~~~~~~~~~~
Swapping two DOM trees is fast (constant time), despite the complexity of the tress.
Swapping two DOM trees is fast (constant time), despite the complexity of the trees.
# What's next {#WhatsNext}
......
This diff is collapsed.
#include "unittest.h"
#include "rapidjson/document.h"
#include <algorithm>
using namespace rapidjson;
......@@ -44,6 +45,58 @@ TEST(Value, assignment_operator) {
EXPECT_EQ(y.GetString(),mstr);
}
template <typename A, typename B>
void TestEqual(const A& a, const B& b) {
EXPECT_TRUE (a == b);
EXPECT_FALSE(a != b);
EXPECT_TRUE (b == a);
EXPECT_FALSE(b != a);
}
template <typename A, typename B>
void TestUnequal(const A& a, const B& b) {
EXPECT_FALSE(a == b);
EXPECT_TRUE (a != b);
EXPECT_FALSE(b == a);
EXPECT_TRUE (b != a);
}
TEST(Value, equalto_operator) {
Value::AllocatorType allocator;
Value x(kObjectType);
x.AddMember("hello", "world", allocator)
.AddMember("t", Value(true).Move(), allocator)
.AddMember("f", Value(false).Move(), allocator)
.AddMember("n", Value(kNullType).Move(), allocator)
.AddMember("i", 123, allocator)
.AddMember("pi", 3.14, allocator)
.AddMember("a", Value(kArrayType).Move().PushBack(1, allocator).PushBack(2, allocator).PushBack(3, allocator), allocator);
// Test templated operator==() and operator!=()
TestEqual(x["hello"], "world");
const char* cc = "world";
TestEqual(x["hello"], cc);
char* c = strdup("world");
TestEqual(x["hello"], c);
free(c);
TestEqual(x["t"], true);
TestEqual(x["f"], false);
TestEqual(x["i"], 123);
TestEqual(x["pi"], 3.14);
// Test operator==()
Value y;
y.CopyFrom(x, allocator);
TestEqual(x, y);
// Swapping member order should be fine.
y.RemoveMember("t");
TestUnequal(x, y);
y.AddMember("t", Value(true).Move(), allocator);
TestEqual(x, y);
}
template <typename Value>
void TestCopyFrom() {
typename Value::AllocatorType a;
......@@ -568,6 +621,76 @@ TEST(Value, Array) {
EXPECT_TRUE(y.Empty());
EXPECT_EQ(0u, y.Size());
// Erase(ValueIterator)
// Use array of array to ensure removed elements' destructor is called.
// [[0],[1],[2],...]
for (int i = 0; i < 10; i++)
x.PushBack(Value(kArrayType).PushBack(i, allocator).Move(), allocator);
// Erase the first
itr = x.Erase(x.Begin());
EXPECT_EQ(x.Begin(), itr);
EXPECT_EQ(9u, x.Size());
for (int i = 0; i < 9; i++)
EXPECT_EQ(i + 1, x[i][0u].GetInt());
// Ease the last
itr = x.Erase(x.End() - 1);
EXPECT_EQ(x.End(), itr);
EXPECT_EQ(8u, x.Size());
for (int i = 0; i < 8; i++)
EXPECT_EQ(i + 1, x[i][0u].GetInt());
// Erase the middle
itr = x.Erase(x.Begin() + 4);
EXPECT_EQ(x.Begin() + 4, itr);
EXPECT_EQ(7u, x.Size());
for (int i = 0; i < 4; i++)
EXPECT_EQ(i + 1, x[i][0u].GetInt());
for (int i = 4; i < 7; i++)
EXPECT_EQ(i + 2, x[i][0u].GetInt());
// Erase(ValueIterator, ValueIterator)
// Exhaustive test with all 0 <= first < n, first <= last <= n cases
const unsigned n = 10;
for (unsigned first = 0; first < n; first++) {
for (unsigned last = first; last <= n; last++) {
x.Clear();
for (unsigned i = 0; i < n; i++)
x.PushBack(Value(kArrayType).PushBack(i, allocator).Move(), allocator);
itr = x.Erase(x.Begin() + first, x.Begin() + last);
if (last == n)
EXPECT_EQ(x.End(), itr);
else
EXPECT_EQ(x.Begin() + first, itr);
size_t removeCount = last - first;
EXPECT_EQ(n - removeCount, x.Size());
for (unsigned i = 0; i < first; i++)
EXPECT_EQ(i, x[i][0u].GetUint());
for (unsigned i = first; i < n - removeCount; i++)
EXPECT_EQ(i + removeCount, x[i][0u].GetUint());
}
}
// Working in gcc without C++11, but VS2013 cannot compile. To be diagnosed.
#if 0
// http://en.wikipedia.org/wiki/Erase-remove_idiom
x.Clear();
for (int i = 0; i < 10; i++)
if (i % 2 == 0)
x.PushBack(i, allocator);
else
x.PushBack(Value(kNullType).Move(), allocator);
x.Erase(std::remove(x.Begin(), x.End(), Value(kNullType)), x.End());
EXPECT_EQ(5u, x.Size());
for (int i = 0; i < 5; i++)
EXPECT_EQ(i * 2, x[i]);
#endif
// SetArray()
Value z;
z.SetArray();
......@@ -683,6 +806,72 @@ TEST(Value, Object) {
EXPECT_TRUE(x.MemberBegin() == x.MemberEnd());
// EraseMember(ConstMemberIterator)
// Use array members to ensure removed elements' destructor is called.
// { "a": [0], "b": [1],[2],...]
const char keys[][2] = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j" };
for (int i = 0; i < 10; i++)
x.AddMember(keys[i], Value(kArrayType).PushBack(i, allocator), allocator);
// Erase the first
itr = x.EraseMember(x.MemberBegin());
EXPECT_FALSE(x.HasMember(keys[0]));
EXPECT_EQ(x.MemberBegin(), itr);
EXPECT_EQ(9u, x.MemberEnd() - x.MemberBegin());
for (; itr != x.MemberEnd(); ++itr) {
int i = (itr - x.MemberBegin()) + 1;
EXPECT_STREQ(itr->name.GetString(), keys[i]);
EXPECT_EQ(i, itr->value[0u].GetInt());
}
// Erase the last
itr = x.EraseMember(x.MemberEnd() - 1);
EXPECT_FALSE(x.HasMember(keys[9]));
EXPECT_EQ(x.MemberEnd(), itr);
EXPECT_EQ(8u, x.MemberEnd() - x.MemberBegin());
for (; itr != x.MemberEnd(); ++itr) {
int i = (itr - x.MemberBegin()) + 1;
EXPECT_STREQ(itr->name.GetString(), keys[i]);
EXPECT_EQ(i, itr->value[0u].GetInt());
}
// Erase the middle
itr = x.EraseMember(x.MemberBegin() + 4);
EXPECT_FALSE(x.HasMember(keys[5]));
EXPECT_EQ(x.MemberBegin() + 4, itr);
EXPECT_EQ(7u, x.MemberEnd() - x.MemberBegin());
for (; itr != x.MemberEnd(); ++itr) {
int i = (itr - x.MemberBegin());
i += (i<4) ? 1 : 2;
EXPECT_STREQ(itr->name.GetString(), keys[i]);
EXPECT_EQ(i, itr->value[0u].GetInt());
}
// EraseMember(ConstMemberIterator, ConstMemberIterator)
// Exhaustive test with all 0 <= first < n, first <= last <= n cases
const unsigned n = 10;
for (unsigned first = 0; first < n; first++) {
for (unsigned last = first; last <= n; last++) {
Value(kObjectType).Swap(x);
for (unsigned i = 0; i < n; i++)
x.AddMember(keys[i], Value(kArrayType).PushBack(i, allocator), allocator);
itr = x.EraseMember(x.MemberBegin() + first, x.MemberBegin() + last);
if (last == n)
EXPECT_EQ(x.MemberEnd(), itr);
else
EXPECT_EQ(x.MemberBegin() + first, itr);
size_t removeCount = last - first;
EXPECT_EQ(n - removeCount, size_t(x.MemberEnd() - x.MemberBegin()));
for (unsigned i = 0; i < first; i++)
EXPECT_EQ(i, x[keys[i]][0u].GetUint());
for (unsigned i = first; i < n - removeCount; i++)
EXPECT_EQ(i + removeCount, x[keys[i+removeCount]][0u].GetUint());
}
}
// SetObject()
Value z;
z.SetObject();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment