skip to Main Content

What is the quickest & most efficient way (memory efficiency) to filter a list of objects into a map with each unique object property being a key with a list of objects with matching object property?

I can’t figure out how to do this without using lots of memory and duplicating the main list. In practice this initial list could have thousands of entries.

void main() {
  
  List<Car> cars = [
    Car('BMW', 5),
    Car('Mercedes', 5),
    Car('Mercedes', 4),
    Car('Mercedes', 2),
    Car('Ferarri', 2),
    Car('Ferarri', 4),
    Car('BMW', 6),
    Car('BMW', 2),
    Car('BMW', 4),
    Car('McLaren', 3),
  ];
  
  Map<String, List<Car>> organizedCars = {};
  
  for (Car car in cars) {
    ???
  }
  
}

class Car {
  String name;
  int passangers;
  
  Car(this.name, this.passangers);
}

Required output:

Map<String, List<Car>> organizedCars = {
'BMW': [
    Car('BMW', 6),
    Car('BMW', 5),
    Car('BMW', 2),
    Car('BMW', 4),
    ],
'Mercedes': [
    Car('Mercedes', 5),
    Car('Mercedes', 4),
    Car('Mercedes', 2),
    ],
etc...
};

2

Answers


  1. You can use fold method to group the items together.

    Here is one impelementation:

    import 'dart:collection';
    
    void main() {
      List<Car> cars = [
        Car('BMW', 5),
        Car('Mercedes', 5),
        Car('Mercedes', 4),
        Car('Mercedes', 2),
        Car('Ferarri', 2),
        Car('Ferarri', 4),
        Car('BMW', 6),
        Car('BMW', 2),
        Car('BMW', 4),
        Car('McLaren', 3),
      ];
    
      final orgnizedCar = cars.fold(
          HashMap<String, List<Car>>(),
          (previousValue, element) => previousValue
            ..update(
              element.name,
              (value) => value..add(element),
              ifAbsent: () => [element],
            ));
    
      print(orgnizedCar);
    }
    
    class Car {
      String name;
      int passangers;
    
      Car(this.name, this.passangers);
    
      @override
      String toString() => "Car('$name', $passangers)";
    }
    
    

    Output:

    {
        Ferarri: [Car('Ferarri', 2), Car('Ferarri', 4)], 
        BMW: [Car('BMW', 5), Car('BMW', 6), Car('BMW', 2), Car('BMW', 4)], 
        Mercedes: [Car('Mercedes', 5), Car('Mercedes', 4), Car('Mercedes', 2)], 
        McLaren: [Car('McLaren', 3)]
    }
    

    Output is formatted as it was single line in console it would not look pretty here.

    Login or Signup to reply.
  2. For Maps that don’t allow nullable values, I prefer using (map[key] ??= []).add(...), which I think is more elegant and more compact than using Map.update(..., ifAbsent: ...). It looks up the List associated with the Map key, initializing the Map value to an empty List if the lookup fails, and then unconditionally appends an element to the found List.

    void main() {
      List<Car> cars = [
        Car('BMW', 5),
        Car('Mercedes', 5),
        Car('Mercedes', 4),
        Car('Mercedes', 2),
        Car('Ferarri', 2),
        Car('Ferarri', 4),
        Car('BMW', 6),
        Car('BMW', 2),
        Car('BMW', 4),
        Car('McLaren', 3),
      ];
    
      Map<String, List<Car>> organizedCars = {};
    
      for (Car car in cars) {
        (organizedCars[car.name] ??= []).add(car);
      }
    
      print(organizedCars);
    }
    
    class Car {
      String name;
      int passangers;
    
      Car(this.name, this.passangers);
    
      @override
      String toString() => "Car('$name', $passangers)";
    }
    

    Which will print (after some reformatting):

    {
      BMW: [
        Car('BMW', 5),
        Car('BMW', 6),
        Car('BMW', 2),
        Car('BMW', 4)
      ],
      Mercedes: [
        Car('Mercedes', 5),
        Car('Mercedes', 4),
        Car('Mercedes', 2)
      ],
      Ferarri: [
        Car('Ferarri', 2),
        Car('Ferarri', 4)
      ],
      McLaren: [
        Car('McLaren', 3)
      ]
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search