Pārlūkot izejas kodu

LibJS: Implement Set.prototype.difference

Idan Horowitz 2 gadi atpakaļ
vecāks
revīzija
be8329d5f6

+ 1 - 0
Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h

@@ -148,6 +148,7 @@ namespace JS {
     P(deleteProperty)                        \
     P(deref)                                 \
     P(description)                           \
+    P(difference)                            \
     P(direction)                             \
     P(disambiguation)                        \
     P(done)                                  \

+ 70 - 0
Userland/Libraries/LibJS/Runtime/SetPrototype.cpp

@@ -34,6 +34,7 @@ void SetPrototype::initialize(Realm& realm)
     define_native_function(realm, vm.names.values, values, 0, attr);
     define_native_function(realm, vm.names.union_, union_, 1, attr);
     define_native_function(realm, vm.names.intersection, intersection, 1, attr);
+    define_native_function(realm, vm.names.difference, difference, 1, attr);
     define_native_accessor(realm, vm.names.size, size_getter, {}, Attribute::Configurable);
 
     define_direct_property(vm.names.keys, get_without_side_effects(vm.names.values), attr);
@@ -313,4 +314,73 @@ JS_DEFINE_NATIVE_FUNCTION(SetPrototype::intersection)
     return result;
 }
 
+// 3 Set.prototype.difference ( other ), https://tc39.es/proposal-set-methods/#sec-set.prototype.difference
+JS_DEFINE_NATIVE_FUNCTION(SetPrototype::difference)
+{
+    // 1. Let O be the this value.
+    // 2. Perform ? RequireInternalSlot(O, [[SetData]]).
+    auto* set = TRY(typed_this_object(vm));
+
+    // 3. Let otherRec be ? GetSetRecord(other).
+    auto other_record = TRY(get_set_record(vm, vm.argument(0)));
+
+    // 4. Let resultSetData be a copy of O.[[SetData]].
+    auto result = set->copy();
+
+    // 5. Let thisSize be the number of elements in O.[[SetData]].
+    auto this_size = set->set_size();
+
+    // 6. If thisSize ≤ otherRec.[[Size]], then
+    if (this_size <= other_record.size) {
+        // a. For each element e of resultSetData, do
+        for (auto& element : *set) {
+            // i. If e is not empty, then
+            // 1.     Let inOther be ToBoolean(? Call(otherRec.[[Has]], otherRec.[[Set]], « e »)).
+            auto in_other = TRY(call(vm, *other_record.has, other_record.set, element.key)).to_boolean();
+            // 2.     If inOther is true, then
+            if (in_other) {
+                // a. Remove e from resultSetData.
+                result->set_remove(element.key);
+            }
+        }
+    }
+    // 7. Else,
+    else {
+        // a. Let keysIter be ? GetKeysIterator(otherRec).
+        auto keys_iterator = TRY(get_keys_iterator(vm, other_record));
+
+        // b. Let next be true.
+        auto next = true;
+
+        // c. Repeat, while next is not false,
+        while (next) {
+            // i. Set next to ? IteratorStep(keysIter).
+            auto* iterator_result = TRY(iterator_step(vm, keys_iterator));
+            next = iterator_result;
+
+            // ii. If next is not false, then
+            if (next) {
+                // 1. Let nextValue be ? IteratorValue(next).
+                auto next_value = TRY(iterator_value(vm, *iterator_result));
+
+                // 2. If nextValue is -0𝔽, set nextValue to +0𝔽.
+                if (next_value.is_negative_zero())
+                    next_value = Value(0);
+
+                // 3. If SetDataHas(resultSetData, nextValue) is true, then
+                if (result->set_has(next_value)) {
+                    // a. Remove nextValue from resultSetData.
+                    result->set_remove(next_value);
+                }
+            }
+        }
+    }
+
+    // 8. Let result be OrdinaryObjectCreate(%Set.prototype%, « [[SetData]] »).
+    // 9. Set result.[[SetData]] to resultSetData.
+
+    // 10. Return result.
+    return result;
+}
+
 }

+ 1 - 0
Userland/Libraries/LibJS/Runtime/SetPrototype.h

@@ -30,6 +30,7 @@ private:
     JS_DECLARE_NATIVE_FUNCTION(values);
     JS_DECLARE_NATIVE_FUNCTION(union_);
     JS_DECLARE_NATIVE_FUNCTION(intersection);
+    JS_DECLARE_NATIVE_FUNCTION(difference);
 
     JS_DECLARE_NATIVE_FUNCTION(size_getter);
 };

+ 12 - 0
Userland/Libraries/LibJS/Tests/builtins/Set/Set.prototype.difference.js

@@ -0,0 +1,12 @@
+test("basic functionality", () => {
+    expect(Set.prototype.difference).toHaveLength(1);
+
+    const set1 = new Set(["a", "b", "c"]);
+    const set2 = new Set(["b", "c", "d", "e"]);
+    const difference1to2 = set1.difference(set2);
+    expect(difference1to2).toHaveSize(1);
+    expect(difference1to2.has("a")).toBeTrue();
+    const difference2to1 = set2.difference(set1);
+    expect(difference2to1).toHaveSize(2);
+    ["d", "e"].forEach(value => expect(difference2to1.has(value)).toBeTrue());
+});