When Modify a Value in the Copied List, It Modify the Value in The Original List Flutter - list

I want to copy list and make no reference even when I edit value in copied list. I tried addAll(), List.from(), map(), toList() and [...myList] but they were unhelpful.
Edit for Clarification
Ex:
class Item{
String description;
int price;
Item(this.description, this.price);
}
List<Item> items = [
Item('item 1', 100),
Item('item 2', 200),
Item('item 3', 300),
];
List<Item> selectedItems = List.from(items);
When I edit selectedItems original list items shouldn't be affected, but it's not?!
selectedItems[0].price = 115;
it modifies the price of element 0 in both.

Flutter haven't copy function for objects, you should create "copy constructor" manually. It's discussed here: How can I clone an Object (deep copy) in Dart?
Solution for your example:
void main() {
// Original list
List<Item> items = [
Item('item 1', 100),
Item('item 2', 200),
Item('item 3', 300),
];
// Copied list
List<Item> selectedItems = items.map((it) => Item.copy(it)).toList();
// Change price for item in copied list
selectedItems[0].price = 115;
print("Original list:");
items.forEach((it) => print("Description: ${it.description}, price: ${it.price}"));
print("Copied list:");
selectedItems.forEach((it) => print("Description: ${it.description}, price: ${it.price}"));
}
class Item {
String description;
int price;
Item(this.description, this.price);
// Copied constructor
Item.copy(Item item) : this(item.description, item.price);
}

Related

Retrieving information from nested lists (Flutter)

I have what appears to most likely be a simple problem for everyone else, yet for some reason I can't seem to fix. I am a complete Flutter noob, but with coding experience. I am hoping to get tips around structuring data, and I have created a list which houses another list, like so:
class Store{
final String name;
final String image;
final List <Product> products; // this is defined in another class, and it requires a name(string), description(string), price (double)
Store({required this.name, required this.image, required this.features, required this.price})
List <Store> store= [
Store(name: "Ikea:, image: "ikealogo.png",
products :[Product(name: "wood table", description: "a very nice wood table", price: 12.50),
Product(name: "comfy chair", description: "a very nice leather chair", price: 10.50),
Product(name: "awesome lamp", description: "an ultra bright lamp", price: 5.50),]),
Store(name: "Bestbuy:, image: "bestbuylogo.png",
products :[Product(name: "television", description: "a very nice television", price: 350.00),
Product(name: "radio", description: "a very loud radio", price: 15.50),
Product(name: "cellphone", description: "a very small phone", price: 78.50),]),
];
}
Basically I have like 20 more of these things, following the same format.
Now for my problem, I can't seem to create a list out of the "Product" info nested within "Store". I can create lists with Store just fine, and I can call on the name and logo for all parts of the UI. My challenge is getting the Product information after a Store is selected, as the code I use currently shows "Iterable" when I hover over it. On the other hand, I get "List" just fine when I define or call the store list on another route.
#override
Widget build(BuildContext context) {
final product = Store.store.map((store) {
final productlist = store.products.toList();
});
I know that my code may not make any sense, and you can give me any kind of recommendations to alter the data structure altogether. For now (without using databases just yet), I want to show what products are available based on the store selected.
I hope everyone has a safe and productive day. Thank you!
In the example below I have set up a drop down button to show the list of stores. Selection causes a repaint which show the products in the selected store.
Hope the helps.
class _MyHomePageState extends State<MyHomePage> {
int _storeIdx = 0;
Store get _currentStore => storeList[_storeIdx];
List<Store> storeList = [
Store(name: "Ikea:", image: "ikealogo.png", products: [
Product(name: "wood table", description: "a very nice wood table", price: 12.50),
Product(name: "comfy chair", description: "a very nice leather chair", price: 10.50),
Product(name: "awesome lamp", description: "an ultra bright lamp", price: 5.50),
]),
Store(name: "Bestbuy:", image: "bestbuylogo.png", products: [
Product(name: "television", description: "a very nice television", price: 350.00),
Product(name: "radio", description: "a very loud radio", price: 15.50),
Product(name: "cellphone", description: "a very small phone", price: 78.50),
]),
];
#override
Widget build(BuildContext context) {
int _offset = 0;
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
DropdownButton(
value: _storeIdx,
items: storeList
.map((Store s) => DropdownMenuItem(
child: Text(s.name),
value: _offset++,
))
.toList(),
onChanged: (int? value) {
setState(() {
_storeIdx = value ?? 0;
});
},
hint: const Text('Select store')),
Text("Currently ${_currentStore.name}"),
..._currentStore.products.map((Product p) => Text(p.name)).toList(),
],
),
),
);
}
}
Note that the spread operator ... is quite useful when you are trying to flatten a hierarchy of widgets.
Also, sometimes in Flutter, there is a tendency to have one humongous build method. Sometimes it is clearer to break a screen in separate widgets or to break the build method into several methods each returning an array of widgets that can then be consolidated in the build method.
I can't help but notice that store inside of Store class is not a static variable which means Store.store should not be accessible. However, if store were a static variable inside Store class Store.store will work.
So,
class Product {
String name;
Product(this.name);
#override
String toString() {
return "Product($name)";
}
}
class Store {
List<Product> products;
Store(this.products);
static List<Store> stores = [
Store([
Product("Soap"),
Product("Bar"),
]),
Store([
Product("Sponge"),
Product("ChocoBar"),
]),
];
}
void main() {
print(Store.stores[0].products);
print(Store.stores[1].products);
}
Will yield an output of :
[Product(Soap), Product(Bar)]
[Product(Sponge), Product(ChocoBar)]
which is what we expect to find.

How to count category value of array of List

let's say I have a list like the example below
<Categories>myList = [
Categories(
nameCategory: 'Book',
amount: '20'
),
Categories(
nameCategory: 'Book',
amount: '40'
),
Categories(
nameCategory: 'Food',
amount: '20'
),
Categories(
nameCategory: 'Food',
amount: '15'
),
];
How I can combine the duplicate values of that list and count the value of the list based on name ??
I can combine the list and the count value of the list but that only works just in a general list like sum total
what I want to do is make a new List but only combine several parts that share the same property like the same category or same class like that
this is an example what I want to achieve
<Categories> anotherList= [
Categories(
nameCategory: 'Book',
amount: '60'
),
Categories(
nameCategory: 'Food',
amount: '35'
),
];
I would replace your List<Categories> with a Map<String, Categories>. Then you can easily look up the Categories object given its name and mutate the existing Categories object. For example, something like:
var mergedCategories = <String, Categories>{};
for (var categories in myList) {
var name = categories.nameCategory;
var amount = categories.amount;
(mergedCategories[name] ??= Categories(nameCategory: name, amount: 0))
.amount += amount;
}
You're essentially trying to get an aggregate value from a list, which is what List.fold is meant to help with.
Here's an example of how you might use it:
class Category {
final String name;
int amount;
Category({required this.name, required this.amount});
String toString() => "Category(name: $name, amount: $amount)";
}
void main() {
final categories = [
Category(
name: 'Book',
amount: 20
),
Category(
name: 'Book',
amount: 40
),
Category(
name: 'Food',
amount: 20
),
Category(
name: 'Food',
amount: 15
),
];
/**
* Here is where the aggregation is done
*/
final List<Category> aggregated = categories.fold([], (list, item) {
try {
// Check whether the category is already in the aggregate
final existingCategory = list.firstWhere((c) => c.name == item.name);
// Category is already in the list, so just add the amount of the current item.
existingCategory.amount += item.amount;
return list;
} catch (_) {
// The category has not yet been added - so add it here
list.add(item);
return list;
}
});
print(aggregated);
}
I've changed your category class a bit for simplicity, but the principle should be the same. You can read more about the fold function here: https://api.dart.dev/stable/2.13.4/dart-core/Iterable/fold.html
A pretty straightforward method is by using the groupBy function provided by the collection.dart package.
import 'package:collection/collection.dart';
groupBy<Categories, String>(list, (c) => c.nameCategory).values.map(
(list) => list.reduce(
(a, b) => new Categories(a.nameCategory, a.amount + b.amount)
)
);

Display items in a list containing a specific name - flutter

In this list, I want to display out all the items that contains this specific name.
My list items: ['US', 'SG', 'US']
print(list.contains("US"));
Using .contains() returns me a true or false but it doesn’t return me the list of strings that contains that. I want to only extract out the items that has 'US' from the list. In this case, there's 2 of it. Please help!
You can try doing it the following way -
List<String> myList = ['US', 'SG', 'US'];
print(myList.where((item) => item.contains("US")));
You can also display it directly inside a Text widget in the following way -
Text(
myList.where((item) => item.contains("US")).join(" "),
//The join function joins the elements of the Iterable into a single string with the separator provided as an argument.
),
Hope this helps!
UPDATE:
To display each of the word separately as a list you can display them inside a Column in the following way -
Column(
children: myList.map((value) {
if(value.contains("US")){
return Text(value,);
} else {
return Container();
//Return an empty Container for non-matching case
}
}).toList(),
)
The same thing can be used inside a ListView instead of Column if you want it to be scrollable.
Something like this?
var myList = ['US', 'SG', 'US'];
myList.forEach((w){
if(w == "US")
print(w);
});
To show:
class SO extends StatelessWidget {
var myList = ['US', 'SG', 'US'];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisSize: MainAxisSize.min,
children: myList.where((w) => w == "US").map((w) => Text(w)).toList(),
),
);
}
}
or for a single line display use, Text instead of Column widget mentioned above
Text(myList.where((w) => w.contains("US")).join(" "))
If you are using "list.contains". It will only show the list exist or not , If you want to print the value you have to use follow :
var fruits = [‘banana’, ‘pineapple’, ‘watermelon’];fruits.forEach((fruit) => print(fruit)); // => banana pineapple watermelon
If you want to print just banana then you can use this
var fruits = [‘banana’, ‘pineapple’, ‘watermelon’];fruits.(fruit) => print(fruit[0]); // => banana

Order list of objects by custom order initialized in other list

i have list a of objects which type is Person :
class Person{
int id
String name
String bestColor
}
def persons = [ new Person(1,'Abdennour','white'),
new Person(2,'Ali','red'),
new Person(3,'Hsen','white'),
new Person(4,'Aicha','green') ]
and i have a list of color:
def colors=['green','white','red']
i want to order persons list according to 3rd field(bestColor) . However, I do not want alphabetical order of colors , but rather I want the same order as colors list.
that it means , the expected result is :
def persons=[new Person(4,'Aicha','green')
,new Person(1,'Abdennour','white')
,new Person(3,'Hsen','white')
,new Person(2,'Ali','red')]
So given:
#groovy.transform.Canonical
class Person{
int id
String name
String bestColor
}
def persons = [ new Person( 1, 'Abdennour', 'white' ),
new Person( 2, 'Ali', 'red' ),
new Person( 3, 'Hsen', 'white' ),
new Person( 4, 'Aicha', 'green' ) ]
def colors = [ 'green','white','red' ]
You can just do:
// Sort (mutating the persons list)
persons.sort { colors.indexOf( it.bestColor ) }
// check it's as expected
assert persons.id == [ 4, 1, 3, 2 ]

Lists AS value of a Map in Dart

I want to create a map of members, but every membres have 3 propreties : first name, last name, and username. How can I create like a list of liste, but with a map.
So I want to have something like :
var membres= {['lastname': 'Bonneau',
'firstname': 'Pierre',
'username': 'mariobross'],
['lastname': 'Hamel',
'firstname': 'Alex',
'username': 'Queenlatifa'],
};
As you know, this code doesn't work. But it explain pretty well what I am trying to do.
I think you are confusing the two constructs here.
Read this introduction to the language: http://www.dartlang.org/docs/dart-up-and-running/ch02.html#lists
A list is a list of elements which can be denoted with the shorthand [...] syntax:
var list = [1, 2, "foo", 3, new Date.now(), 4];
Whereas a map can be denoted with the curly brace shorthand syntax:
var gifts = { // A map literal
// Keys Values
'first' : 'partridge',
'second' : 'turtledoves',
'fifth' : 'golden rings'
};
So, let's modify your code to work:
var members = [
{
'lastname': 'Bonneau',
'firstname': 'Pierre',
'username': 'mariobross'
},
{
'lastname': 'Hamel',
'firstname': 'Alex',
'username': 'Queenlatifa'
}
];
You can, for example, print the information like this:
members.forEach((e) {
print(e['firstname']);
});
If I understand your intent correctly, you want to have a list of maps. What you have is correct except you confused [ and {. The following works:
var membres = [
{'lastname': 'Bonneau',
'firstname': 'Pierre',
'username': 'mariobross'},
{'lastname': 'Hamel',
'firstname': 'Alex',
'username': 'Queenlatifa'}
];
As an example, to get a list of all usernames:
print(membres.map((v) => v['username']));
If you don't really need a Map, what about using a class to improve the structure of your code :
class Member {
String firstname;
String lastname;
String username;
Member(this.firstname, this.lastname, this.username);
}
main() {
final members = new List<Member>();
members.add(new Member('Pierre', 'Bonneau', 'mariobross'));
members.add(new Member('Alex', 'Hamel', 'Queenlatifa'));
// use members
}
You mean like this?
// FirstName => LastName => Value
var lookup = new Map<String, Map<String, String>>();
// get / set values like this
void setValue(String firstName, String lastName, String value) {
if (!lookUp.containsKey(firstName))
lookUp[firstName] = new Map<String, String>();
lookUp[firstName][lastName] = value;
}
String getValue(String firstName, String lastName) {
if (!lookUp.containsKey(firstName)) return "";
return lookUp[firstName][lastName];
}
First of all you need to create a map with value as list. Dont forget to initialize it
then if you want to fill it you first need to use built in function like putIfAbsent as in dart to add first object in list and then use update to add items in list. therefore you will need two arrays. First to put elements and then to add elements in list with same key. Also you can use try catch to identify if the key is present or not to do that in one loop
for (var item in days) {
var date_time = DateTime.parse(item["date"] + " 00:00:00");
_events[date_time] = _events.putIfAbsent(
date_time,
() => [
{
"title": item["title"],
"date": item["date"],
"time": reUse.get_time_am_pm_format(item["time"]),
"feature": item["feature"],
}
]);
}
for (var item in days) {
var date_time = DateTime.parse(item["date"] + " 00:00:00");
_events[date_time] = _events.update(date_time, (value) {
value.add({
"title": item["title"],
"date": item["date"],
"time": reUse.get_time_am_pm_format(item["time"]),
"feature": item["feature"],
});
return value;
});
}