diff --git a/docs/src/dev/provider/gremlin-semantics.asciidoc b/docs/src/dev/provider/gremlin-semantics.asciidoc index 1ef5dfe65dd..d16f0a99721 100644 --- a/docs/src/dev/provider/gremlin-semantics.asciidoc +++ b/docs/src/dev/provider/gremlin-semantics.asciidoc @@ -1484,7 +1484,7 @@ link:https://tinkerpop.apache.org/docs/x.y.z/reference/#rTrim-step[reference] *Arguments:* * `separator` - The string character(s) used as delimiter to split the input string. Nullable, a null separator will split on -whitespaces. +whitespaces. An empty string separator will split on each character. * `scope` - Determines the type of traverser it operates on. Both scopes will operate on the level of individual traversers. The `global` scope will operate on individual string traverser. The `local` scope will operate on list traverser with string elements inside. diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc index a40af88ed2a..d34f4430206 100644 --- a/docs/src/reference/the-traversal.asciidoc +++ b/docs/src/reference/the-traversal.asciidoc @@ -4451,8 +4451,8 @@ link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gre [[split-step]] === Split Step The `split()`-step (*map*) returns a list of strings created by splitting the incoming string traverser around the -matches of the given separator. A null separator will split the string by whitespaces. Null values from the incoming -traversers are not processed and remain as null when returned. If the incoming traverser is a non-String value then an +matches of the given separator. A null separator will split the string by whitespaces. An empty string separator will split on each character. +Null values from the incoming traversers are not processed and remain as null when returned. If the incoming traverser is a non-String value then an IllegalArgumentException will be thrown. [gremlin-groovy,modern] @@ -4460,12 +4460,14 @@ IllegalArgumentException will be thrown. g.inject("that", "this", "test", null).split("h") <1> g.V().hasLabel("person").values("name").split("a") <2> g.inject("helloworld", "hello world", "hello world").split(null) <3> -g.V().hasLabel("person").values("name").fold().split(local, "a") <4> +g.inject("hello", "world", null).split("") <4> +g.V().hasLabel("person").values("name").fold().split(local, "a") <5> ---- <1> Split the strings by "h". <2> Split person names by "a". <3> Splitting by null will split by whitespaces. -<4> Use `Scope.local` to operate on individual string elements inside incoming list, which will return a list of results. +<4> Splitting by "" will split by each character. +<5> Use `Scope.local` to operate on individual string elements inside incoming list, which will return a list of results. *Additional References* link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.html#split(java.lang.String)++[`split(String)`] diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SplitGlobalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SplitGlobalStep.java index 14e886edfed..fba728a50b2 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SplitGlobalStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SplitGlobalStep.java @@ -18,15 +18,13 @@ */ package org.apache.tinkerpop.gremlin.process.traversal.step.map; -import org.apache.commons.lang3.StringUtils; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.Traverser; import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent; import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; +import org.apache.tinkerpop.gremlin.util.StringUtil; -import java.util.Arrays; import java.util.Collections; -import java.util.List; import java.util.Set; /** @@ -57,7 +55,7 @@ protected E map(final Traverser.Admin traverser) { } // we will pass null values to next step - return null == item? null : (E) Arrays.asList(StringUtils.splitByWholeSeparator((String) item, this.separator)); + return null == item? null : (E) StringUtil.split((String)item, this.separator); } public String getSeparator() { @@ -75,4 +73,5 @@ public int hashCode() { result = 31 * result + (null != this.separator ? this.separator.hashCode() : 0); return result; } + } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SplitLocalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SplitLocalStep.java index 5d638c74ca6..9576e22b655 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SplitLocalStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SplitLocalStep.java @@ -18,17 +18,10 @@ */ package org.apache.tinkerpop.gremlin.process.traversal.step.map; -import org.apache.commons.lang3.StringUtils; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; -import org.apache.tinkerpop.gremlin.process.traversal.Traverser; import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent; import org.apache.tinkerpop.gremlin.process.traversal.step.util.StringLocalStep; -import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Set; +import org.apache.tinkerpop.gremlin.util.StringUtil; /** * Reference implementation for substring step, a mid-traversal step which returns a list of strings created by @@ -50,7 +43,7 @@ public SplitLocalStep(final Traversal.Admin traversal, final String separator) { @Override protected E applyStringOperation(String item) { - return (E) Arrays.asList((StringUtils.splitByWholeSeparator(item, this.separator))); + return (E) StringUtil.split(item, this.separator); } @Override diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/StringUtil.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/StringUtil.java new file mode 100644 index 00000000000..d352a38df48 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/StringUtil.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.util; + +import java.util.Arrays; +import java.util.List; +import org.apache.commons.lang3.StringUtils; + +/** + * Utility class for Strings. + * + * @author Andrea Child + */ +public final class StringUtil { + + private StringUtil() { + } + + /** + * Splits the provided string into a List of string by the given separator, which will not be included in the returned List. + * + * @param toSplit the string to split + * @param separator the separator string to split by - empty will split into a List of characters + * @return a List of parsed strings, not including the given separator + */ + public static List split(final String toSplit, final String separator) { + if (toSplit == null) { + throw new IllegalArgumentException("toSplit cannot be null"); + } + if (StringUtils.EMPTY.equals(separator)) { + // split into a list of character strings + // ie. "ab cd" -> ["a","b"," ","c","d"] + return Arrays.asList(toSplit.split(StringUtils.EMPTY)); + } + return Arrays.asList(StringUtils.splitByWholeSeparator(toSplit, separator)); + } +} diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SplitGlobalStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SplitGlobalStepTest.java index 9a442fa5f0b..ceb0598ffd5 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SplitGlobalStepTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SplitGlobalStepTest.java @@ -48,7 +48,7 @@ public void testReturnTypes() { assertArrayEquals(new String[]{"helloworld"}, __.__("helloworld").split(null).next().toArray()); assertArrayEquals(new String[]{"hello", "world"}, __.__("hello world").split(null).next().toArray()); assertArrayEquals(new String[]{"hello", "world"}, __.__("hello world").split(null).next().toArray()); - assertArrayEquals(new String[]{"hello", "world"}, __.__("hello world").split("").next().toArray()); + assertArrayEquals(new String[]{"h","e","l","l","o"," ", "w","o","r","l","d"}, __.__("hello world").split("").next().toArray()); assertArrayEquals(new String[]{"hello", "world"}, __.__("hello world").split(" ").next().toArray()); assertArrayEquals(new String[]{"hello world"}, __.__("hello world").split(" ").next().toArray()); diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SplitLocalStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SplitLocalStepTest.java index b9c792d7d40..cddf7dc4e09 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SplitLocalStepTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SplitLocalStepTest.java @@ -42,6 +42,7 @@ protected List getTraversals() { public void testReturnTypes() { assertArrayEquals(new String[]{"h", "llo"}, __.__("hello").split(Scope.local, "e").next().toArray()); assertArrayEquals(new String[]{"ab", "bc"}, __.__("abc|abc").split(Scope.local, "c|a").next().toArray()); + assertArrayEquals(new String[]{"t","e","s","t"}, __.__("test").split(Scope.local, "").next().toArray()); List> resList = new ArrayList<>(); resList.add(Arrays.asList("helloworld")); diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/StringUtilTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/StringUtilTest.java new file mode 100644 index 00000000000..04e112f92d1 --- /dev/null +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/StringUtilTest.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.util; + +import java.util.Arrays; +import org.apache.tinkerpop.gremlin.AssertHelper; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author Andrea Child + */ +public class StringUtilTest { + + @Test + public void shouldBeUtilityClass() throws Exception { + AssertHelper.assertIsUtilityClass(StringUtil.class); + } + + @Test + public void shouldSplitBySeparatorAndNotContainSeparator() { + assertEquals(Arrays.asList("h","llo"), StringUtil.split("hello", "e")); + } + + @Test + public void shouldNotBeSplitIfSeparatorNotPresentInString() { + final String toSplit = "test"; + assertEquals(Arrays.asList(toSplit), StringUtil.split(toSplit, "x")); + } + + @Test + public void shouldSplitByCharacterIfSeparatorIsEmpty() { + assertEquals(Arrays.asList("t","e","s","t"," ","1","2","3"), StringUtil.split("test 123", "")); + } + + @Test + public void shouldSplitByWhitespaceIfSeparatorIsNull() { + assertEquals(Arrays.asList("test","123"), StringUtil.split("test 123", null)); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldThrowIfStringToSplitIsNull() { + StringUtil.split(null, "test"); + } +} \ No newline at end of file diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs index 521f7fb11b9..e01f7663fdc 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs @@ -1204,9 +1204,11 @@ private static IDictionary, ITraversal>> {(g,p) =>g.V().Has("name","vadas").ShortestPath().With("~tinkerpop.shortestPath.distance","weight").With("~tinkerpop.shortestPath.maxDistance",1.3)}}, {"g_injectXthat_this_testX_spiltXhX", new List, ITraversal>> {(g,p) =>g.Inject("that","this","test",null).Split("h")}}, {"g_injectXhello_worldX_spiltXnullX", new List, ITraversal>> {(g,p) =>g.Inject("hello world").Split(null)}}, + {"g_injectXthat_this_test_nullX_splitXemptyX", new List, ITraversal>> {(g,p) =>g.Inject("that","this","test",null).Split("")}}, {"g_injectXListXa_bXcX_splitXa_bX", new List, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).Split("a")}}, {"g_V_hasLabelXpersonX_valueXnameX_splitXnullX", new List, ITraversal>> {(g,p) =>g.V().HasLabel("person").Values("name").Split(null)}}, {"g_V_hasLabelXpersonX_valueXnameX_order_fold_splitXlocal_aX_unfold", new List, ITraversal>> {(g,p) =>g.V().HasLabel("person").Values("name").Order().Fold().Split(Scope.Local,"a").Unfold()}}, + {"g_V_hasLabelXpersonX_valueXnameX_order_fold_splitXlocal_emptyX_unfold", new List, ITraversal>> {(g,p) =>g.V().HasLabel("person").Values("name").Order().Fold().Split(Scope.Local,"").Unfold()}}, {"g_injectXthat_this_testX_substringX1_8X", new List, ITraversal>> {(g,p) =>g.Inject("test","hello world",null).Substring(1,8)}}, {"g_injectXListXa_bXcX_substringX1_2X", new List, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).Substring(1,2)}}, {"g_V_hasLabelXpersonX_valueXnameX_substringX2X", new List, ITraversal>> {(g,p) =>g.V().HasLabel("software").Values("name").Substring(2)}}, diff --git a/gremlin-go/driver/cucumber/gremlin.go b/gremlin-go/driver/cucumber/gremlin.go index d2867d7f88f..7e0a5ccdf16 100644 --- a/gremlin-go/driver/cucumber/gremlin.go +++ b/gremlin-go/driver/cucumber/gremlin.go @@ -1175,9 +1175,11 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_V_hasXname_vadasX_shortestPath_distanceXweightX_maxDistanceX1_3X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Has("name", "vadas").ShortestPath().With("~tinkerpop.shortestPath.distance", "weight").With("~tinkerpop.shortestPath.maxDistance", 1.3)}}, "g_injectXthat_this_testX_spiltXhX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("that", "this", "test", nil).Split("h")}}, "g_injectXhello_worldX_spiltXnullX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("hello world").Split(nil)}}, + "g_injectXthat_this_test_nullX_splitXemptyX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("that", "this", "test", nil).Split("")}}, "g_injectXListXa_bXcX_splitXa_bX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"]).Split("a")}}, "g_V_hasLabelXpersonX_valueXnameX_splitXnullX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("person").Values("name").Split(nil)}}, "g_V_hasLabelXpersonX_valueXnameX_order_fold_splitXlocal_aX_unfold": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("person").Values("name").Order().Fold().Split(gremlingo.Scope.Local, "a").Unfold()}}, + "g_V_hasLabelXpersonX_valueXnameX_order_fold_splitXlocal_emptyX_unfold": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("person").Values("name").Order().Fold().Split(gremlingo.Scope.Local, "").Unfold()}}, "g_injectXthat_this_testX_substringX1_8X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("test", "hello world", nil).Substring(1, 8)}}, "g_injectXListXa_bXcX_substringX1_2X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"]).Substring(1, 2)}}, "g_V_hasLabelXpersonX_valueXnameX_substringX2X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("software").Values("name").Substring(2)}}, diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js index d93e55473ac..1060fdbda3f 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js @@ -1195,9 +1195,11 @@ const gremlins = { g_V_hasXname_vadasX_shortestPath_distanceXweightX_maxDistanceX1_3X: [function({g}) { return g.V().has("name","vadas").shortestPath().with_("~tinkerpop.shortestPath.distance","weight").with_("~tinkerpop.shortestPath.maxDistance",1.3) }], g_injectXthat_this_testX_spiltXhX: [function({g}) { return g.inject("that","this","test",null).split("h") }], g_injectXhello_worldX_spiltXnullX: [function({g}) { return g.inject("hello world").split(null) }], + g_injectXthat_this_test_nullX_splitXemptyX: [function({g}) { return g.inject("that","this","test",null).split("") }], g_injectXListXa_bXcX_splitXa_bX: [function({g, xx1}) { return g.inject(xx1).split("a") }], g_V_hasLabelXpersonX_valueXnameX_splitXnullX: [function({g}) { return g.V().hasLabel("person").values("name").split(null) }], g_V_hasLabelXpersonX_valueXnameX_order_fold_splitXlocal_aX_unfold: [function({g}) { return g.V().hasLabel("person").values("name").order().fold().split(Scope.local,"a").unfold() }], + g_V_hasLabelXpersonX_valueXnameX_order_fold_splitXlocal_emptyX_unfold: [function({g}) { return g.V().hasLabel("person").values("name").order().fold().split(Scope.local,"").unfold() }], g_injectXthat_this_testX_substringX1_8X: [function({g}) { return g.inject("test","hello world",null).substring(1,8) }], g_injectXListXa_bXcX_substringX1_2X: [function({g, xx1}) { return g.inject(xx1).substring(1,2) }], g_V_hasLabelXpersonX_valueXnameX_substringX2X: [function({g}) { return g.V().hasLabel("software").values("name").substring(2) }], diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py index c01921d018b..4c1f095d580 100644 --- a/gremlin-python/src/main/python/radish/gremlin.py +++ b/gremlin-python/src/main/python/radish/gremlin.py @@ -1177,9 +1177,11 @@ 'g_V_hasXname_vadasX_shortestPath_distanceXweightX_maxDistanceX1_3X': [(lambda g:g.V().has('name','vadas').shortestPath().with_('~tinkerpop.shortestPath.distance','weight').with_('~tinkerpop.shortestPath.maxDistance',float(1.3)))], 'g_injectXthat_this_testX_spiltXhX': [(lambda g:g.inject('that','this','test',None).split('h'))], 'g_injectXhello_worldX_spiltXnullX': [(lambda g:g.inject('hello world').split(None))], + 'g_injectXthat_this_test_nullX_splitXemptyX': [(lambda g:g.inject('that','this','test',None).split(''))], 'g_injectXListXa_bXcX_splitXa_bX': [(lambda g, xx1=None:g.inject(xx1).split('a'))], 'g_V_hasLabelXpersonX_valueXnameX_splitXnullX': [(lambda g:g.V().hasLabel('person').name.split(None))], 'g_V_hasLabelXpersonX_valueXnameX_order_fold_splitXlocal_aX_unfold': [(lambda g:g.V().hasLabel('person').name.order().fold().split(Scope.local,'a').unfold())], + 'g_V_hasLabelXpersonX_valueXnameX_order_fold_splitXlocal_emptyX_unfold': [(lambda g:g.V().hasLabel('person').name.order().fold().split(Scope.local,'').unfold())], 'g_injectXthat_this_testX_substringX1_8X': [(lambda g:g.inject('test','hello world',None).substring(1,8))], 'g_injectXListXa_bXcX_substringX1_2X': [(lambda g, xx1=None:g.inject(xx1).substring(1,2))], 'g_V_hasLabelXpersonX_valueXnameX_substringX2X': [(lambda g:g.V().hasLabel('software').name.substring(2))], diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Split.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Split.feature index 54e04f14baf..3fa024adcd3 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Split.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Split.feature @@ -45,6 +45,21 @@ Feature: Step - split() | result | | l[hello,world] | + @GraphComputerVerificationInjectionNotSupported + Scenario: g_injectXthat_this_test_nullX_splitXemptyX + Given the empty graph + And the traversal of + """ + g.inject("that", "this", "test", null).split("") + """ + When iterated to list + Then the result should be unordered + | result | + | l[t,h,a,t] | + | l[t,h,i,s] | + | l[t,e,s,t] | + | null | + @GraphComputerVerificationInjectionNotSupported Scenario: g_injectXListXa_bXcX_splitXa_bX Given the empty graph @@ -83,3 +98,17 @@ Feature: Step - split() | l[m,rko] | | l[peter] | | l[v,d,s] | + + Scenario: g_V_hasLabelXpersonX_valueXnameX_order_fold_splitXlocal_emptyX_unfold + Given the modern graph + And the traversal of + """ + g.V().hasLabel("person").values("name").order().fold().split(Scope.local, "").unfold() + """ + When iterated to list + Then the result should be unordered + | result | + | l[j,o,s,h] | + | l[m,a,r,k,o] | + | l[p,e,t,e,r] | + | l[v,a,d,a,s] | \ No newline at end of file