Skip to content

Commit

Permalink
Add map methods for getting keys, values, and entries (#173)
Browse files Browse the repository at this point in the history
  • Loading branch information
zxch3n authored Nov 16, 2023
1 parent 8c3e8e5 commit 40d6de3
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 32 deletions.
71 changes: 70 additions & 1 deletion crates/loro-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1246,7 +1246,76 @@ impl LoroMap {
self.0.get(key).into()
}

/// Get the keys and values without being resolved recursively.
/// Get the keys of the map.
///
/// @example
/// ```ts
/// import { Loro } from "loro-crdt";
///
/// const doc = new Loro();
/// const map = doc.getMap("map");
/// map.set("foo", "bar");
/// map.set("baz", "bar");
/// const keys = map.keys(); // ["foo", "baz"]
/// ```
pub fn keys(&self) -> Vec<JsValue> {
let mut ans = Vec::with_capacity(self.0.len());
self.0.for_each(|k, v| {
if v.value.is_some() {
ans.push(k.to_string().into());
}
});
ans
}

/// Get the values of the map.
///
/// @example
/// ```ts
/// import { Loro } from "loro-crdt";
///
/// const doc = new Loro();
/// const map = doc.getMap("map");
/// map.set("foo", "bar");
/// map.set("baz", "bar");
/// const values = map.values(); // ["bar", "bar"]
/// ```
pub fn values(&self) -> Vec<JsValue> {
let mut ans: Vec<JsValue> = Vec::with_capacity(self.0.len());
self.0.for_each(|_, v| {
if let Some(v) = &v.value {
ans.push(v.clone().into());
}
});
ans
}

/// Get the entries of the map.
///
/// @example
/// ```ts
/// import { Loro } from "loro-crdt";
///
/// const doc = new Loro();
/// const map = doc.getMap("map");
/// map.set("foo", "bar");
/// map.set("baz", "bar");
/// const entries = map.entries(); // [["foo", "bar"], ["baz", "bar"]]
/// ```
pub fn entries(&self) -> Vec<JsValue> {
let mut ans: Vec<JsValue> = Vec::with_capacity(self.0.len());
self.0.for_each(|k, v| {
if let Some(v) = &v.value {
let array = Array::new();
array.push(&k.to_string().into());
array.push(&v.clone().into());
ans.push(array.into());
}
});
ans
}

/// Get the keys and values shallowly
///
/// {@link LoroMap.getDeepValue}
///
Expand Down
88 changes: 57 additions & 31 deletions loro-js/tests/basic.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
import { describe, expect, it } from "vitest";
import {
ContainerID,
Loro,
LoroList,
LoroMap,
setPanicHook,
} from "../src";
import { ContainerID, Loro, LoroList, LoroMap, setPanicHook } from "../src";

setPanicHook();

Expand All @@ -21,35 +15,35 @@ it("basic example", () => {
map.set("key", "value");
expect(doc.toJson()).toStrictEqual({
list: ["A", "B", "C"],
map: { key: "value" }
map: { key: "value" },
});

// delete 2 elements at index 0
list.delete(0, 2)
list.delete(0, 2);
expect(doc.toJson()).toStrictEqual({
list: ["C"],
map: { key: "value" }
map: { key: "value" },
});

// Insert a text container to the list
const text = list.insertContainer(0, "Text");
text.insert(0, "Hello");
text.insert(0, "Hi! ")
text.insert(0, "Hi! ");

// delete 1 element at index 0
expect(doc.toJson()).toStrictEqual({
list: ["Hi! Hello", "C"],
map: { key: "value" }
map: { key: "value" },
});

// Insert a list container to the map
const list2 = map.setContainer("test", "List");
list2.insert(0, 1);
expect(doc.toJson()).toStrictEqual({
list: ["Hi! Hello", "C"],
map: { key: "value", test: [1] }
map: { key: "value", test: [1] },
});
})
});

it("basic sync example", () => {
const docA = new Loro();
Expand All @@ -61,28 +55,26 @@ it("basic sync example", () => {
// B import the ops from A
docB.import(docA.exportFrom());
expect(docB.toJson()).toStrictEqual({
list: ["A", "B", "C"]
})
list: ["A", "B", "C"],
});

const listB: LoroList = docB.getList("list");
// delete 1 element at index 1
listB.delete(1, 1);
// A import the ops from B
docA.import(docB.exportFrom(docA.version()))
docA.import(docB.exportFrom(docA.version()));
// list at A is now ["A", "C"], with the same state as B
expect(docA.toJson()).toStrictEqual({
list: ["A", "C"]
list: ["A", "C"],
});
expect(docA.toJson()).toStrictEqual(docB.toJson());
})
});

it("basic events", () => {
const doc = new Loro();
doc.subscribe(event => {

});
doc.subscribe((event) => {});
const list = doc.getList("list");
})
});

describe("list", () => {
it("insert containers", () => {
Expand All @@ -95,13 +87,13 @@ describe("list", () => {
expect(typeof v).toBe("string");
const m = doc.getMap(v as ContainerID);
expect(m.getDeepValue()).toStrictEqual({ key: "value" });
})
});

it.todo("iterate");
})
});

describe("import", () => {
it('pending', () => {
it("pending", () => {
const a = new Loro();
a.getText("text").insert(0, "a");
const b = new Loro();
Expand All @@ -111,13 +103,47 @@ describe("import", () => {
c.import(b.exportFrom());
c.getText("text").insert(2, "c");

// c export from b's version, which cannot be imported directly to a.
// c export from b's version, which cannot be imported directly to a.
// This operation is pending.
a.import(c.exportFrom(b.version()))
a.import(c.exportFrom(b.version()));
expect(a.getText("text").toString()).toBe("a");

// a import the missing ops from b. It makes the pending operation from c valid.
a.import(b.exportFrom(a.version()))
a.import(b.exportFrom(a.version()));
expect(a.getText("text").toString()).toBe("abc");
})
})
});
});

describe("map", () => {
it("keys", () => {
const doc = new Loro();
const map = doc.getMap("map");
map.set("foo", "bar");
map.set("baz", "bar");
const entries = map.keys();
expect(entries).toStrictEqual(["foo", "baz"]);
});

it("values", () => {
const doc = new Loro();
const map = doc.getMap("map");
map.set("foo", "bar");
map.set("baz", "bar");
const entries = map.values();
expect(entries).toStrictEqual(["bar", "bar"]);
});

it("entries", () => {
const doc = new Loro();
const map = doc.getMap("map");
map.set("foo", "bar");
map.set("baz", "bar");
map.set("new", 11);
map.delete("new");
const entries = map.entries();
expect(entries).toStrictEqual([
["foo", "bar"],
["baz", "bar"],
]);
});
});

0 comments on commit 40d6de3

Please sign in to comment.