Add map methods for getting keys, values, and entries (#173)

This commit is contained in:
Zixuan Chen 2023-11-16 18:40:51 +08:00 committed by GitHub
parent 8c3e8e531e
commit 40d6de3146
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 127 additions and 32 deletions

View file

@ -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}
///

View file

@ -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();
@ -21,25 +15,25 @@ 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
@ -47,9 +41,9 @@ it("basic example", () => {
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();
@ -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", () => {
@ -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();
@ -113,11 +105,45 @@ describe("import", () => {
// 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"],
]);
});
});