Coming from a Java background: what is the recommended way to "clone" a Dart List, Map and Set?
Use of clone() in Java is tricky and questionable1,2. Effectively, clone() is a copy constructor and for that, the Dart List, Map and Set types each have a named constructor named .from() that perform a shallow copy; e.g. given these declarations
Map<String, int> numMoons, moreMoons;
numMoons = const <String,int>{ 'Mars' : 2, 'Jupiter' : 27 };
List<String> planets, morePlanets;
you can use .from() like this:
moreMoons = new Map<String,int>.from(numMoons)
..addAll({'Saturn' : 53 });
planets = new List<String>.from(numMoons.keys);
morePlanets = new List<String>.from(planets)
..add('Pluto');
Note that List.from() more generally accepts an iterator rather than just a List.
For sake of completeness, I should mention that the dart:html Node class defines a clone() method.
1 J. Bloch, "Effective Java" 2nd Ed., Item 11.
2 B. Venners, "Josh Bloch on Design: Copy Constructor versus Cloning", 2002. Referenced from here3. Quote from the article:
If you've read the item about cloning in my book, especially if you read between the lines, you will know that I think clone is deeply broken. ---J.Bloch
3 Dart Issue #6459, clone instance(object).
With the new version of dart cloning of a Map or List become quite easy.
You can try this method for making a deep clone of List and Map.
For List
List a = ['x','y', 'z'];
List b = [...a];
For Maps
Map mapA = {"a":"b"};
Map mapB = {...mapA};
For Sets
Set setA = {1,2,3,};
Set setB = {...setA};
I hope someone find this helpful.
If you are using dart > 2.3.0, You can use spread operator something like:
List<int> a = [1,2,3];
List<int> b = [...a]; // copy of a
For lists and sets, I typically use
List<String> clone = []..addAll(originalList);
The caveat, as #kzhdev mentions, is that addAll() and from()
[Do] not really make a clone. They add a reference in the new Map/List/Set.
That's usually ok with me, but I would keep it in mind.
For deep copy (clone), you can use :
Map<String, dynamic> src = {'a': 123, 'b': 456};
Map<String, dynamic> copy = json.decode(json.encode(src));
but there may be some concerns about the performance.
This solution should work:
List list1 = [1,2,3,4];
List list2 = list1.map((element)=>element).toList();
It's for a list but should work the same for a map etc, remember to add to list if its a list at the end
Map.from() only works for 1D map.
To copy multi dimensional map without reference in dart use following method
Map<keyType, valueType> copyDeepMap( Map<keyType, valueType> map )
{
Map<keyType, valueType> newMap = {};
map.forEach
(
(key, value)
{
newMap[key] =( value is Map ) ? copyDeepMap(value) : value ;
}
);
return newMap;
}
Method-1: Recommended
For cloning a multi-dimensional (nested) List or Map, use the
json.decode() and json.encode()
List newList = json.decode(json.encode(oldList));
Map newMap = json.decode(json.encode(oldList));
Method-2:
List newList = [...oldList];
Map newMap = {...oldMap}
Method-3:
List newList = List.from(oldList);
Map newMap = Map.from(oldMap);
Method-4:
List newList = List.of(oldList);
Map newMap = Map.of(oldMap);
Method-5:
List newList = List.unmodifiable(oldList);
Map newMap = Map.unmodifiable(oldMap);
For more References:
https://www.kindacode.com/article/how-to-clone-a-list-or-map-in-dart-and-flutter/
https://coflutter.com/dart-flutter-how-to-clone-copy-a-list/
Best solution for me:
List temp = {1,2,3,4}
List platforms = json.decode(json.encode(parent.platforms));
This was my solution. I hope it can help someone.
factory Product.deepCopy(Product productToCopy) => new Product(
productToCopy.id,
productToCopy.title,
productToCopy.description,
productToCopy.price,
productToCopy.imageUrl,
productToCopy.isFavorite,
);}
To copy Map<String, List> filtered;
var filteredNewCopy = filtered.map((key, value) => MapEntry(key, [...value]));
There is no 100% bullet proof way of making an exact isolated copy, but the answer from Manish Dhruw is pretty good. However, it will only work for Maps containing simple variable types and nested Maps.
To extend it to also work with other common collections, such as List and Set, and combinations of them, you could use something like the code below.
You don't really need the DeepCopyable class, but it would be useful if you want to easily make your own classes "deep-copyable" with these functions.
abstract class DeepCopyable{
T deepCopy<T>();
}
List<T> listDeepCopy<T>(List list){
List<T> newList = List<T>();
list.forEach((value) {
newList.add(
value is Map ? mapDeepCopy(value) :
value is List ? listDeepCopy(value) :
value is Set ? setDeepCopy(value) :
value is DeepCopyable ? value.deepCopy() :
value
);
});
return newList;
}
Set<T> setDeepCopy<T>(Set s){
Set<T> newSet = Set<T>();
s.forEach((value) {
newSet.add(
value is Map ? mapDeepCopy(value) :
value is List ? listDeepCopy(value) :
value is Set ? setDeepCopy(value) :
value is DeepCopyable ? value.deepCopy() :
value
);
});
return newSet;
}
Map<K,V> mapDeepCopy<K,V>(Map<K,V> map){
Map<K,V> newMap = Map<K,V>();
map.forEach((key, value){
newMap[key] =
value is Map ? mapDeepCopy(value) :
value is List ? listDeepCopy(value) :
value is Set ? setDeepCopy(value) :
value is DeepCopyable ? value.deepCopy() :
value;
});
return newMap;
}
As I mentioned, it's obviously still not 100% bullet proof - for example you will loose type information for nested collections.
List<int> a = [1,2,3];
List<int> b = a.toList(); // copy of a
Seems to work too
**Dart 2.15
This was my solution,hope it works for you
class Person {
String? name;
int? age;
Person(this.name, this.age);
factory Person.clone(Person source) {
return Person(source.name, source.age);
}
}
final personList = [
Person('Tom', 22),
Person('Jane', 25),
];
final yourCopy = personList.map((p) => Person.clone(p)).toList();
If you are working with dynamic typed data (aka sourced from JSON or built to encode as JSON) you can use this function to perform a deep copy:
cloneDeep(value) {
if (value is List<dynamic>) {
return value.map<dynamic>(
(item) => cloneDeep(item),
).toList();
} else if (value is Map) {
return value.map<String, dynamic>(
(key, item) => MapEntry<String, dynamic>(key, cloneDeep(item)));
}
return value;
}
final list = [[],[]];
final cloned = list.copyRange(0, list.length-1);
best solution is use of toMap() and fromMap() for classes.
class A {
String title;
A(Map<String, Object> map){ // fromMap() or fromJson()
title= map['title'];
}
Map<String, Object> toMap(){
final res = <String, Object>{};
res['title'] = title;
return res;
}
}
The given answer is good, but be aware of the generate constructor which is helpful if you want to "grow" a fixed length list, e.g.:
List<String> list = new List<String>(5);
int depth = 0; // a variable to track what index we're using
...
depth++;
if (list.length <= depth) {
list = new List<String>.generate(depth * 2,
(int index) => index < depth ? list[index] : null,
growable: false);
}
Related
I got such a map
Map<String, List<Object>>
when I use
map.toSortedMap(comparator)
and comparing values by keys I got sorted map but amount of objects in each value list is also changed (reduced). It's super strange. Under the hood toSortedMap method creates TreeMap with the given comparator, I don't know TreeMap internals but I assume that any map should not change values but only manipulate with the keys. Is this something that is ok for TreeMap or is it a bug in Kotlin library?
Edit
Code I get this issue on:
val categoriesKeys = listOf("key1", "key2", etc)
val categoriesKeysOrdersMap = mapOf("key1" to 0, "key2" to 1, etc)
val model = listOf(MyModel(..), MyModel(..), etc)
val categoryKeyToModelsMap = categoriesKeys
.asSequence()
.map { key ->
key to model.filter {
it.categories?.contains(key) == true
}
}
.filter { it.second.isNotEmpty() }
.toMap()
.toSortedMap(compareBy { categoriesKeysOrdersMap[it] })
On toMap step I have the map I want but not sorted, but after toSortedMap my data is messed up. Comparator compares by int value from categoriesKeysOrdersMap using key from category.
I have a the following container in Java that I need to work on
Map<String, List<Entry<Parameter, String>>>
Where Parameter is an enumerated type defined as follows:
public enum Parameter {
Param1,
Param2,
Param3
}
The code below shows how I initialize the map structure - effectively putting 2 rows in the container.
Map<String, List<Entry<Parameter, String>>> map2 = new HashMap<String, List<Entry<Parameter, String>>>() {{
put("SERVICE1", new ArrayList<Entry<Parameter, String>>(){{
add (new AbstractMap.SimpleEntry<>(Parameter.Param1,"val1"));
add (new AbstractMap.SimpleEntry<>(Parameter.Param2,"val2"));
add (new AbstractMap.SimpleEntry<>(Parameter.Param3,"val3"));
}});
put("SERVICE2", new ArrayList<Entry<Parameter, String>>(){{
add (new AbstractMap.SimpleEntry<>(Parameter.Param1,"val4"));
add (new AbstractMap.SimpleEntry<>(Parameter.Param2,"val5"));
add (new AbstractMap.SimpleEntry<>(Parameter.Param3,"val6"));
}});
}};
I need to use the java 8 streams api to find the val1 and val2 values from "SERVICE1" but I do not know the correct java streams filter and mapping syntax.
The nearest thing I can come up with is the following, but this only filters at the top level and it returns a list of lists rather than the list of Parameter.Param1,"val1" & Parameter.Param2,"val3" that I am looking for from the SERVICE1 row.
List<List<Entry<Parameter, String>>> listOfLists = myMap.entrySet().stream()
.filter(next -> next.getKey().equals("SERVICE1"))
.map(Map.Entry::getValue)
.collect(Collectors.toList());
listOfLists.size();
If you only need the "val1" and "val2" values, you can first use getOrDefault to get the corresponding list, and then filter on the entries' keys to get entries with Param1 or Param2 as key, and finally apply map again to get the values of these entries.
List<String> list =
myMap.getOrDefault("SERVICE1", Collections.emptyList())
.stream()
.filter(e -> e.getKey() == Parameter.Param1 || e.getKey() == Parameter.Param2)
.map(Map.Entry::getValue)
.collect(Collectors.toList());
Also you might be interested to look at Efficiency of Java "Double Brace Initialization"?
Well, I have a problem with creating expression tree for SelectMany.. especially at the typeArguments part..
So, I have a database with tables like below:
[Group] (one to many) [GroupDetail] (many to one) [Item] (one to many) [ItemDetail]
GroupDetail.group is a Group
GroupDetail.item is an Item
ItemDetail.item is an Item
Item.itemDetail is a collection of ItemDetail
Group.groupDetail is a collection of GroupDetail
so you can see that group detail is simply a many to many link for Group and Item
and (one to many) is a one to many relation..
For example, the data is like below:
Group, GroupDetail, Item, ItemDetail
------------------------------------
gr1, grDt1, ItemA, PartsAA
gr1, grDt1, ItemA, PartsAB
gr1, grDt2, ItemB, PartsBA
gr1, grDt2, ItemB, PartsBB
gr2, grDt3, ItemC, PartsCA
gr2, grDt4, ItemA, PartsAA
gr2, grDt4, ItemA, PartsAB
gr3, grDt4, ItemD, PartsDA
gr3, grDt5, ItemE, PartsEA
I want to select items and each of it's detail by a group search
and return it as a collection of some sort of view class..
Similar like this function below:
public IQueryable<ItemGroupDetailView> getViewQ(IQueryable<GroupDetail> myQ)
{
return myQ.SelectMany(
m => m.item.itemDetail,
(m, n) => new ItemGroupDetailView
{
groupName = m.group.name,
groupDetailCount = m.group.groupDetail.Count,
item = new ItemView
{
itemName = n.item.name,
itemDetailCount = n.item.itemDetail.Count
},
itemDetail = new ItemDetailView
{
itemDetailName = n.name
}
}
);
}
simply like that above BUT I want it to be a dynamic exp tree instead, so maybe I can just use it like:
Filter filter = new Filter("gr1","ItemA"); // just a filter
var myQ = getSearchQ(filters); // it gets all the where etc, everything is fine here..
var viewQ = getViewQ(myQ); // simply to convert the data to the view,.. where all the errors are
var finalQ = ApplyLimit(ApplyGrouping(ApplySorting(ApplySelect(myQ))); // paging, sorting, grouping, etc..
// run the select.. get the count etc..
now I want to make it dynamic but I seems to get it wrong on the SelectMany part
This is roughly how I do the SelectMany things:
step 1: I bind the property/field assignment.. it came from some sort of list-string-configuration-kinda-thing that map the assignment
PropertyInfo pInfo;
MemberExpression mExp;
// parse getproperty reflect etc...
List<MemberAssignment> memberAssginments = new List<MemberAssignment>();
memberAssginments.Add(Expression.Bind(pInfo, mExp);
step 2: then the usual member init
MemberInitExpression mie =
Expression.MemberInit(Expression.New
(typeof(ItemGroupDetailView)), memberAssginments);
so I get this:
new ItemGroupDetailView
{
groupName = m.group.name,
groupDetailCount = m.group.groupDetail.Count,
item = new ItemView
{
itemName = n.item.name,
itemDetailCount = n.item.itemDetail.Count
},
itemDetail = new ItemDetailView
{
itemDetailName = n.name
}
}
step 3: then get the expression collectionSelector & resultSelector
ParamterExpression m = Expression.Parameter(typeof(GroupDetail),"m");
ParamterExpression n = Expression.Parameter(typeof(ItemDetail),"n");
Expression<Func<GroupDetail, ItemDetail, ItemGroupDetailView>> exp2 =
Expression.Lambda<Func<GroupDetail, ItemDetail, ItemGroupDetailView>>
(mie, new ParameterExpression[] { m, n });
I think I get what I need, exp2 (resultSelector):
(m, n) => new ItemGroupDetailView
{
groupName = m.group.name,
groupDetailCount = m.group.groupDetail.Count,
item = new ItemView
{
itemName = n.item.name,
itemDetailCount = n.item.itemDetail.Count
},
itemDetail = new ItemDetailView
{
itemDetailName = n.name
}
}
and with similar way I get the other clause, exp1 (collectionSelector)
MemberExpression mEx = .... reflect get property/field etc..
Expression<Func<GroupDetail, IEnumerable<ItemDetail>>> exp1 =
Expression.Lambda<Func<GroupDetail, IEnumerable<ItemDetail>>>(mEx, m);
so I get this:
m => m.item.itemDetail
step 4: then get the selectMany MethodCallExpression itself
MethodCallExpression selectManyExp =
Expression.Call(
typeof(Queryable),
"SelectMany",
new Type[] {
typeof(GroupDetail),
typeof(Expression<Func<GroupDetail, IEnumerable<ItemDetail>>>),
typeof(Expression<Func<GroupDetail, ItemDetail, ItemGroupDetailView>>)
},
new Expression[] { Expression.Quote(exp1), Expression.Quote(exp2) }
);
It doesnt work at all..
(No generic method 'SelectMany' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.)
so I think the main question here is:
How do I build an expression tree for such selectMany query
How do I build an expression query that has resultSelector & collectionSelector and multiple parameters..
and why does the code below works, but the Expression.Call always error..
myQ.SelectMany(exp1, exp2);
I guess I dont understand how SelectMany or Expression Tree works.. :(
but, I need this to be dynamic because the property/field assignment binding and source, selector and result is dynamic
public IQueryable<TView> getViewQ(IQueryable<T> myQ)
{
// some code..
}
EDIT 1 :
Switching the exp1 and exp2.. now exp1 is the collectionSelector and exp2 is the resultSelector..
EDIT 2 :
Furthermore I tried several things:
first, I change the type argument like what Mike said below, but the error still the same
MethodCallExpression selectManyExp =
Expression.Call(
typeof(Queryable),
"SelectMany",
new Type[] {
typeof(GroupDetail),
typeof(ItemDetail),
typeof(ItemGroupDetailView)
},
new Expression[] { Expression.Quote(exp1), Expression.Quote(exp2) }
);
then I try some reflection this and that to check
System.Reflection.MethodInfo sminfo = null;
System.Reflection.MethodInfo sminfo2 = null;
IEnumerable<System.Reflection.MethodInfo> sminfos = typeof(Queryable)
.GetMethods(System.Reflection.BindingFlags.Static
| System.Reflection.BindingFlags.Public)
.Where(xxx => xxx.Name.Equals("SelectMany"));
foreach (System.Reflection.MethodInfo mi in sminfos)
{
if (mi.GetParameters().Count() == 3)
{
sminfo = mi;
}
}
/*
I ran this step by step to make sure that the method I get in sminfo is:
public static IQueryable<TResult> SelectMany<TSource, TCollection, TResult>(
this IQueryable<TSource> source,
Expression<Func<TSource, IEnumerable<TCollection>>> collectionSelector,
Expression<Func<TSource, TCollection, TResult>> resultSelector
);
*/
sminfo2 = sminfo.MakeGenericMethod(
new Type[] {
typeof(GroupDetail), typeof(ItemDetail), typeof(ItemGroupDetailView)
});
MethodCallExpression selectManyExp =
Expression.Call(sminfo2, new Expression[] { exp1, exp2 });
and I get different error:
(Incorrect number of arguments supplied for call to method ..)
and it tells me that the method required 3 parameters instead of 2, and the one I miss is the IQueryable<GroupDetail> source
so I get back to the Expression.Call and add the source parameter
MethodCallExpression selectManyExp =
Expression.Call(
typeof(Queryable),
"SelectMany",
new Type[] {
typeof(GroupDetail),
typeof(ItemDetail),
typeof(ItemGroupDetailView)
},
new Expression[] { myQ.Expression, exp1, exp2 }
);
return (IQueryable<ItemGroupDetailView>)myQ.Provider.CreateQuery(selectManyExp);
and it works.. :D
Sorry for the messy and long post,.. my English is bad.. :(
It looks like you've conflated the type parameters with the formal parameters. I believe your type arguments should look like below:
MethodCallExpression selectManyExp =
Expression.Call(
typeof(Queryable),
"SelectMany",
new Type[] {
typeof(GroupDetail),
typeof(ItemDetail),
typeof(ItemGroupDetailView)
},
new Expression[] { Expression.Quote(exp1), Expression.Quote(exp2) }
);
I've looked at this other question, but can't get my select box to work correctly:
Binding initial/default value of dropdown (select) list
I've got the following Game object:
function Game(visitingTeamDetails, homeTeamDetails, game) {
if (arguments.length > 0) {
this.VisitingTeamDetails = visitingTeamDetails;
this.HomeTeamDetails = homeTeamDetails;
this.GameId = ko.observable(game.GameId);
this.HomeTeamName = ko.observable(game.HomeTeamName);
this.VisitingTeamName = ko.observable(game.VisitingTeamName);
this.SportTypeName = ko.observable(game.SportTypeName);
this.HomeAccountName = ko.observable(game.HomeAccountName);
this.VisitingAccountName = ko.observable(game.VisitingAccountName);
this.GameDateString = ko.observable(game.GameDateString);
this.GameTimeString = ko.observable(game.GameTimeString);
this.AvailableSportTypes = ko.observableArray(game.Sports);
this.sportTypeFunction = function () {
for (sportType in this.AvailableSportTypes()) {
if (this.AvailableSportTypes()[sportType].Name == this.SportTypeName()) {
return this.AvailableSportTypes()[sportType];
}
}
return null;
};
this.SportType = ko.observable(game.SportType);
}
}
SportType is an object with Name and SportTypeId.
I have the following template:
<td rowspan="3"><select data-bind="options: AvailableSportTypes, value: SportType, optionsText:'Name', optionsCaption: 'Choose...'" class="sportType"></select></td>
AvailableSportTypes is a list of SportType.
The list is coming in with the names of the SportTypes in the drop down list, but I can't make the initial selection be SportType. I wrote sportTypeFunction to show myself that the data was coming in correctly, and it would select the correct value, but changing my selection in the drop down would not update SportType.
I'm sure I'm doing something wrong. Anyone see it?
Thanks
When game.SportType gets passed in, it needs to be a reference to the an item in the game.AvailableSportTypes and not just an object that looks the same.
Basically two objects are not equal unless they are actually a reference to the same object.
var a = { name: "test" },
b = { name: "test" };
alert(a === b); //false
So, you would need to call your function to locate the correct object in the array and set it as the value of your observable.
Not that it is way better, but in KO 1.3 you can extend .fn of observables, observableArrays, and dependentObservables to add additional functionality.
Here is a sample: http://jsfiddle.net/rniemeyer/ZP79w
I'm trying to write a trait (in Scala 2.8) that can be mixed in to a case class, allowing its fields to be inspected at runtime, for a particular debugging purpose. I want to get them back in the order that they were declared in the source file, and I'd like to omit any other fields inside the case class. For example:
trait CaseClassReflector extends Product {
def getFields: List[(String, Any)] = {
var fieldValueToName: Map[Any, String] = Map()
for (field <- getClass.getDeclaredFields) {
field.setAccessible(true)
fieldValueToName += (field.get(this) -> field.getName)
}
productIterator.toList map { value => fieldValueToName(value) -> value }
}
}
case class Colour(red: Int, green: Int, blue: Int) extends CaseClassReflector {
val other: Int = 42
}
scala> val c = Colour(234, 123, 23)
c: Colour = Colour(234,123,23)
scala> val fields = c.getFields
fields: List[(String, Any)] = List((red,234), (green,123), (blue,23))
The above implementation is clearly flawed because it guesses the relationship between a field's position in the Product and its name by equality of the value on those field, so that the following, say, will not work:
Colour(0, 0, 0).getFields
Is there any way this can be implemented?
Look in trunk and you'll find this. Listen to the comment, this is not supported: but since I also needed those names...
/** private[scala] so nobody gets the idea this is a supported interface.
*/
private[scala] def caseParamNames(path: String): Option[List[String]] = {
val (outer, inner) = (path indexOf '$') match {
case -1 => (path, "")
case x => (path take x, path drop (x + 1))
}
for {
clazz <- getSystemLoader.tryToLoadClass[AnyRef](outer)
ssig <- ScalaSigParser.parse(clazz)
}
yield {
val f: PartialFunction[Symbol, List[String]] =
if (inner.isEmpty) {
case x: MethodSymbol if x.isCaseAccessor && (x.name endsWith " ") => List(x.name dropRight 1)
}
else {
case x: ClassSymbol if x.name == inner =>
val xs = x.children filter (child => child.isCaseAccessor && (child.name endsWith " "))
xs.toList map (_.name dropRight 1)
}
(ssig.symbols partialMap f).flatten toList
}
}
Here's a short and working version, based on the example above
trait CaseClassReflector extends Product {
def getFields = getClass.getDeclaredFields.map(field => {
field setAccessible true
field.getName -> field.get(this)
})
}
In every example I've seen the fields are in reverse order: the last item in the getFields array is the first one listed in the case class. If you use case classes "nicely", then you should just be able to map productElement(n) onto getDeclaredFields()( getDeclaredFields.length-n-1).
But this is rather dangerous, as I don't know of anything in the spec that insists that it must be that way, and if you override a val in the case class, it won't even appear in getDeclaredFields (it'll appear in the fields of that superclass).
You might change your code to assume things are this way, but check that the getter method with that name and the productIterator return the same value and throw an exception if they don't (which means that you don't actually know what corresponds to what).
You can also use the ProductCompletion from the interpreter package to get to attribute names and values of case classes:
import tools.nsc.interpreter.ProductCompletion
// get attribute names
new ProductCompletion(Colour(1, 2, 3)).caseNames
// returns: List(red, green, blue)
// get attribute values
new ProductCompletion(Colour(1, 2, 3)).caseFields
Edit: hints by roland and virtualeyes
It is necessary to include the scalap library which is part of the scala-lang collection.
Thanks for your hints, roland and virtualeyes.