Home > Mobile >  How to filter one array into multiple arrays depending on a specific value inside of the array?
How to filter one array into multiple arrays depending on a specific value inside of the array?

Time:05-18

I am trying to filter an array of places into multiple arrays depending on the city, state (which I have labeled as address) of the place. I want all of the place objects with the same address to be grouped into one array so I can use that address as a header for each section in my tableView. I just don't know how to do it. I am not sure how to show any code for this but I have tried filteredPlaces.append(places.filter({ $0.address })) That didn't work at all.

I tried to loop through the places array, but I wasn't sure what code I needed in order to identify if any of the objects had the same address.

My current filteredPlaces array looks like this: var filteredPlaces = [[Place]()] and my current array that is holding all of the places, looks like this: var places = [Place]()

Before this gets down voted, please tell me what I can do to explain better because I am not sure how else to ask this question more clearly. How can I create separate arrays based on the address of each place in one array? Thanks for your help!

EDIT TO ADD: Here is my place object class

class Place {
    
    var name: String
    var street: String
    var city: String
    var state: String
    var zip: String
    var drinks: [Drink]
    
    var address: String {
        return "\(city), \(state)"
    }
    
    init(name: String, street: String, city: String, state: String, zip: String, drinks: [Drink]) {
        self.name = name.uppercased()
        self.street = street.uppercased()
        self.city = city.uppercased()
        self.state = state.uppercased()
        self.drinks = drinks
        self.zip = zip
        
    }
    
    convenience init(name: String, street: String, city: String, state: String, zip: String, drinks: [Drink]?) {
        if drinks != nil {
            self.init(name: name, street: street, city: city, state: state, zip: zip, drinks: drinks)
        } else {
            self.init(name: name, street: street, city: city, state: state, zip: zip, drinks: [Drink]())
        }
    }
    
}

I haven't created the declaration yet because that will be done when a user enters the places on a new viewController.

CodePudding user response:

You can use Dictionary(grouping:by:)

The example is like

///     let students = ["Kofi", "Abena", "Efua", "Kweku", "Akosua"]
///     let studentsByLetter = Dictionary(grouping: students, by: { $0.first! })
///     // ["E": ["Efua"], "K": ["Kofi", "Kweku"], "A": ["Abena", "Akosua"]]

In your case you need something hashable for the key of the dictionary, so a hashable pair of place and street names:

  struct PlaceNameStreet: Hashable {
     let name: String
     let street: String
  }


  let dictionary = Dictionary(grouping: places) { place in
     PlaceNameStreet(name: place.name, street: place.street)
  }

CodePudding user response:

you could try something like this, to obtain an dictionary of adresses with Places.

EDIT-1: using your Place class.

// example Places, 2 in Tokyo, and 3 in Sydney
        var places = [
            Place(name: "place-1", street: "street-1", city: "Tokyo", state: "Kanto", zip: "123", drinks: []),
            Place(name: "place-2", street: "street-2", city: "Tokyo", state: "Kanto", zip: "123", drinks: []),
            Place(name: "place-1", street: "street-1", city: "Sydney", state: "NSW", zip: "123", drinks: []),
            Place(name: "place-2", street: "street-2", city: "Sydney", state: "NSW", zip: "123", drinks: []),
            Place(name: "place-3", street: "street-3", city: "Sydney", state: "NSW", zip: "123", drinks: [])]

// just for info 
let adrss = Set(places.map{$0.address})
print("\n---> unique adresses: \(adrss) \n")

// returns a dictionary where the key is the address and the value are the Places
let allPlaces: [String:[Place]] = Set(places.map{$0.address}).reduce(into: [:]) { dict, adrs in
    dict[adrs] = places.filter{$0.address == adrs}
}

 // an array of all places in Sydney
 print("\n---> all Places in Sydney: \(allPlaces["Sydney"]) \n")
 // an array of all places in Tokyo
 print("\n---> all Places in Tokyo: \(allPlaces["Tokyo"]) \n")

The term Set(places.map{$0.address}) gives you all unique addresses. the .reduce(into: [:]), accumulates the Places for each address. The result is a dictionary, with the key=the address and the value=the array of Place

Example usage:

struct ContentView: View {
    @State var allPlaces: [String:[Place]] = [:]
    
    var body: some View {
        ForEach(allPlaces.keys.sorted(), id: \.self) { key in
            HStack {
                if let plcs = allPlaces[key] {
                    Text("\(plcs.count) places in")
                } else {
                    Text("no places in")
                }
                Text(key).foregroundColor(.blue)
            }
        }
        .onAppear {
            // example Places, 2 in Tokyo, and 3 in Sydney
            var places = [
                Place(name: "place-1", street: "street-1", city: "Tokyo", state: "Kanto", zip: "123", drinks: []),
                Place(name: "place-2", street: "street-2", city: "Tokyo", state: "Kanto", zip: "123", drinks: []),
                Place(name: "place-1", street: "street-1", city: "Sydney", state: "NSW", zip: "123", drinks: []),
                Place(name: "place-2", street: "street-2", city: "Sydney", state: "NSW", zip: "123", drinks: []),
                Place(name: "place-3", street: "street-3", city: "Sydney", state: "NSW", zip: "123", drinks: [])]
            
            // returns a dictionary where the key is the address and the value are the Places
            allPlaces = Set(places.map{$0.address}).reduce(into: [:]) { dict, adrs in
                dict[adrs] = places.filter{$0.address == adrs}
            }
        }
    }
}
  • Related