Swift how to filter 2 array - arrays

I'm new in Swift, and I can't figure out how to filter these 2 arrays
var arrayOfFavoriteRoomsId = ["1", "2"]
var arrayOfRooms = [
VoiceRoom(id: "1", title: "Room1", description:"Test room1"),
VoiceRoom(id: "2", title: "Room2", description:"Test room2"),
VoiceRoom(id: "3", title: "Room3", description:"Test room3")
]
The final array should look like this
var filteredArray = [
VoiceRoom(id: "1", title: "Room1", description:"Test room1"),
VoiceRoom(id: "2", title: "Room2", description:"Test room2")
]
This is what my model looks like
struct VoiceRoom: Identifiable, Decodable {
var id: String
var title: String
var description: String
}

arrayOfRooms.filter { room in
arrayOfFavoriteRoomsId.contains(room.id)
}
If you want to sort them as well:
arrayOfRooms.filter { room in
arrayOfFavoriteRoomsId.contains(room.id)
}.sorted(by: { $0.id < $1.id })

Related

How to combine elements in an array?

I'm trying to combine the "role" parameter for "Project" objects with the same "id" and "title" parameters in the myProjects array below:
struct Project: Identifiable {
var id: String
var title: String
var role: String
}
var myProjects = [Project(id: "1", title: "Sunset", role: "2nd AD"),
Project(id: "2", title: "Lights", role: "Mix Tech"),
Project(id: "2", title: "Lights", role: "Sound Mixer"),
Project(id: "3", title: "Beach", role: "Producer")]
var updatedProjects: [Project] = []
// The goal is to update myProjects to show the following:
updatedProjects = [Project(id: "1", title: "Sunset", role: "2nd AD"),
Project(id: "2", title: "Lights", role: "Mix Tech & Sound Mixer"),
Project(id: "3", title: "Beach", role: "Producer"]
This is what I have attempted so far, the result is giving me duplicate combinations of the roles parameter for each project in the myProjects array.
var dupProjects = myProjects
for myProject in myProjects {
for dupProject in dupProjects {
if myProject.id == dupProject.id {
let combinedRoles = "\(myProject.role) & \(dupProject.role)"
updatedProjects.append(Project(id: myProject.id,
title: myProject.title,
role: combinedRoles))
}
}
}
print(updatedProjects)
// [__lldb_expr_48.Project(id: "1", title: "Sunset", role: "2nd AD & 2nd AD"),
__lldb_expr_48.Project(id: "2", title: "Lights", role: "Mix Tech & Mix Tech"),
__lldb_expr_48.Project(id: "2", title: "Lights", role: "Mix Tech & Sound Mixer"),
__lldb_expr_48.Project(id: "2", title: "Lights", role: "Sound Mixer & Mix Tech"),
__lldb_expr_48.Project(id: "2", title: "Lights", role: "Sound Mixer & Sound Mixer"),
__lldb_expr_48.Project(id: "3", title: "Beach", role: "Producer & Producer")]
You can use a dictionary to group them by id, combine the roles, then convert the group back to a single Project
let combined = Dictionary(grouping: myProjects) { element in
return element.id
}.compactMapValues { projects -> Project? in
var first = projects.first
first?.role = projects.map { $0.role }.joined(separator: " & ")
return first
}.values.map { $0 }

Dynamically merge array objects

I want to combine two arrays (ranking and matches) that has common properties:
var ranking = [{
def: "0.58",
league: "Scottish Premiership",
name: "Celtic",
off: "3.33",
grank: "3",
tform: "96.33",
},
{
def: "2.52",
league: "Scottish Premiership",
name: "Dundee",
off: "1.28",
grank: "302",
tform: "27.51",
}]
var matches = [{
date: "2010-04-22",
league: "Scottish Premiership",
home: "0.0676",
away: "0.8",
draw: "0.1324",
goals1: "3",
goals2: "1",
tform1: "96.33",
tform2: "27.51",
team1: "Celtic",
team2: "Dundee",}]
Expected output looks like this:
[{
date: "2010-04-22",
league: "Scottish Premiership",
home: "0.0676",
away: "0.8",
draw: "0.1324",
goals1: "3",
goals2: "1",
tform1: "96.33",
tform2: "27.51",
def1: "0.58",
def2: "2.52",
off1: "3.33",
off2: "1.28",
grank1: "3",
grank2: "302",
team1: "Celtic",
team2: "Dundee",}]
To merge the arrays, I used Lodash _.merge function
var result = _.merge(ranking, matches);
The output it returned did merge some objects and omitted homogeneous objects.
Please I need some help and insight in achieving this task. I wouldn't mind any javascript (client-side) solution.
You need to merge by the given data and add the information of the two teams.
const
keys = ['def', 'off', 'grank'],
getRanking = (team, suffix) => Object
.fromEntries(keys.map(k => [k + suffix, team[k]])),
ranking = [{ def: "0.58", league: "Scottish Premiership", name: "Celtic", off: "3.33", grank: "3", tform: "96.33" }, { def: "2.52", league: "Scottish Premiership", name: "Dundee", off: "1.28", grank: "302", tform: "27.51" }],
matches = [{ date: "2010-04-22", league: "Scottish Premiership", home: "0.0676", away: "0.8", draw: "0.1324", goals1: "3", goals2: "1", tform1: "96.33", tform2: "27.51", team1: "Celtic", team2: "Dundee" }],
teams = Object.fromEntries(ranking.map(o => [o.name, o])),
result = matches.map(o => ({
...o,
...getRanking(teams[o.team1], 1),
...getRanking(teams[o.team2], 2)
}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Show random images with their names from an array. (SwiftUI)

New to swiftUI, and I need to show the images and their names randomly by action of a button. How should I call the pictures' array to show a random picture each time user tap on the button in the contentView file ?
this is the array of pictures that I want to show them randomly in contentView(pictures placed in Assets folder):
struct aPicture: Identifiable {
var id: Int
var name: String
var imageName: String
}
let pictures = [
aPicture(id: 0, name: "1", imageName: "1"),
aPicture(id: 1, name: "2", imageName: "2"),
aPicture(id: 2, name: "3", imageName: "3"),
aPicture(id: 3, name: "4", imageName: "4"),
aPicture(id: 4, name: "5", imageName: "5"),
aPicture(id: 5, name: "6", imageName: "6"),
]
you can try this:
struct ContentView: View {
struct aPicture: Identifiable {
var id: Int
var name: String
var imageName: String
}
#State var random : Int = 0
let pictures = [
aPicture(id: 0, name: "1", imageName: "1"),
aPicture(id: 1, name: "2", imageName: "2"),
aPicture(id: 2, name: "3", imageName: "3"),
aPicture(id: 3, name: "4", imageName: "4"),
aPicture(id: 4, name: "5", imageName: "5"),
aPicture(id: 5, name: "6", imageName: "6"),
]
var body: some View {
VStack {
HStack {
Spacer()
Text(pictures[self.random].name)
.background(Color.white)
Spacer()
Button("Next image") {
self.random = Int.random(in: 0..<self.pictures.count)
}
Spacer()
}
Image(pictures[self.random].imageName)
.resizable()
.scaledToFit()
}
}
}
I suggest you different approach.
1) you can generate random element directly
let picture = pictures.randomElement() ?? default_if_empty_collection
2) user would like to see different picture after tap, which could not be true. Less pictures in your "store", more likely randomly you generate the same picture (which could looks like "nothing happens on tap")
next snippet shows how to solve this. On every tap the user see different picture, even though there are just tree pictures in our collection.
import SwiftUI
struct ContentView: View {
#State var img = Image(systemName: "questionmark.square.fill")
let imgs = [Image(systemName: "trash"),
Image(systemName: "trash.fill"),
Image(systemName: "trash.slash"),
]
var body: some View {
img
.resizable()
.scaledToFit()
.frame(maxWidth: .infinity)
.onTapGesture {
var tmp: Image
repeat {
tmp = self.imgs.randomElement() ?? self.img
} while tmp == self.img
self.img = tmp
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Swift 5 group and split array of objects based on object value

I have a sorted array
let things = [
Thing(activity: "1", name: "value1"),
Thing(activity: "1", name: "value2"),
Thing(activity: "1", name: "value3"),
Thing(activity: "2", name: "value4"),
Thing(activity: "2", name: "value5"),
Thing(activity: "3", name: "value6"),
Thing(activity: "3", name: "value7"),
Thing(activity: "1", name: "value8"),
Thing(activity: "1", name: "value9"),
Thing(activity: "1", name: "value10")
]
I would like to produce array of arrays splitted when the activity value changes similar to the following
[[Thing(activity: "1", name: "value1"),
Thing(activity: "1", name: "value2"),
Thing(activity: "1", name: "value3")],
[Thing(activity: "2", name: "value4"),
Thing(activity: "2", name: "value5")],
[Thing(activity: "3", name: "value6"),
Thing(activity: "3", name: "value7")],
[Thing(activity: "1", name: "value8"),
Thing(activity: "1", name: "value9"),
Thing(activity: "1", name: "value10")]]
A generalized solution would be:
extension Sequence {
func grouped<T: Equatable>(by block: (Element) throws -> T) rethrows -> [[Element]] {
return try reduce(into: []) { result, element in
if let lastElement = result.last?.last, try block(lastElement) == block(element) {
result[result.index(before: result.endIndex)].append(element)
} else {
result.append([element])
}
}
}
}
Then you can do:
let results = things.grouped { $0.activity }
A less elegant (but slightly more efficient) solution would be:
extension Sequence {
func grouped<T: Equatable>(by block: (Element) throws -> T) rethrows -> [[Element]] {
var results: [[Element]] = []
var lastValue: T?
var index = results.endIndex
for element in self {
let value = try block(element)
if let lastValue = lastValue, lastValue == value {
results[index].append(element)
} else {
results.append([element])
index = results.index(before: results.endIndex)
lastValue = value
}
}
return results
}
}
As already mentioned by #matt in comments you can use collection method reduce(into:) to group your elements by checking if the activity of the last element of the last array is equal to the current element activity, if so just append a new element to the last array, otherwise append a new array with a single element to the outer array:
struct Thing {
let activity, name: String
}
let things: [Thing] = [
.init(activity: "1", name: "value1"),
.init(activity: "1", name: "value2"),
.init(activity: "1", name: "value3"),
.init(activity: "2", name: "value4"),
.init(activity: "2", name: "value5"),
.init(activity: "3", name: "value6"),
.init(activity: "3", name: "value7"),
.init(activity: "1", name: "value8"),
.init(activity: "1", name: "value9"),
.init(activity: "1", name: "value10")]
let grouped: [[Thing]] = things.reduce(into: []) {
$0.last?.last?.activity == $1.activity ?
$0[$0.index(before: $0.endIndex)].append($1) :
$0.append([$1])
}
print(grouped) // "[[__lldb_expr_1.Thing(activity: "1", name: "value1"), __lldb_expr_1.Thing(activity: "1", name: "value2"), __lldb_expr_1.Thing(activity: "1", name: "value3")], [__lldb_expr_1.Thing(activity: "2", name: "value4"), __lldb_expr_1.Thing(activity: "2", name: "value5")], [__lldb_expr_1.Thing(activity: "3", name: "value6"), __lldb_expr_1.Thing(activity: "3", name: "value7")], [__lldb_expr_1.Thing(activity: "1", name: "value8"), __lldb_expr_1.Thing(activity: "1", name: "value9"), __lldb_expr_1.Thing(activity: "1", name: "value10")]]\n"

fetch array values using sports_id

I have a function that returns an array of objects data.sportdata. I would like to get all array elements with the same sports_id. The code
$scope.arrSportData = data.sportdata;
angular.forEach($scope.arrSportData, function(value, key) {
console.log($scope.arrSportData);
//getting reponse
/*
Object { id: "1", user_id: "2", sport_id: "1", position_id: "1", team_name: "JimmyTmname",}
Object { id: "2", user_id: "2", sport_id: "2", position_id: "6", team_name: "JimmyTmname2",}
Object { id: "3", user_id: "2", sport_id: "3", position_id: "12", team_name: "JimmyTmname3",}
Object { id: "4", user_id: "2", sport_id: "5", position_id: "20", team_name: "JimmyTmname5",}
*/
//code i wrote
if (value.sport_id == 1) {
$scope.positionId.spr1 = value.position_id;
$scope.teamname.spr1 = value.team_name;
}
if (value.sport_id == 2) {
$scope.positionId.spr2 = value.position_id;
$scope.teamname.spr2 = value.team_name;
}
if (value.sport_id == 3) {
$scope.positionId.spr3 = value.position_id;
$scope.teamname.spr3 = value.team_name;
}
if (value.sport_id == 4) {
$scope.positionId.spr4 = value.position_id;
$scope.teamname.spr4 = value.team_name;
}
});
Here I am always getting first value and nothing more. Please suggest and help to solve this problem.
I did try outside of the loop but does not work. I think filter function can do this but dont know how does it work.
Perform a groupBy function which will give you an object like this
{
key: [],
key: []
}
Here the key will be sport_id and [] will be the items with the same key.
A minimal working example will be,
// Written By: Ceaser Bautista
//Link: http://stackoverflow.com/a/34890276/17447
var groupBy = function(xs, key) {
return xs.reduce(function(rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
//here we have four teams with two sports id 13 and 22
var arrSportData = [{
id: "1",
user_id: "1",
sport_id: "13",
position_id: "1",
team_name: "JimmyTmname"
}, {
id: "2",
user_id: "2",
sport_id: "22",
position_id: "6",
team_name: "JimmyTmname2"
}, {
id: "3",
user_id: "3",
sport_id: "22",
position_id: "12",
team_name: "JimmyTmname2",
}, {
id: "4",
user_id: "4",
sport_id: "13",
position_id: "20",
team_name: "JimmyTmname1"
}];
$scope.groupedData = groupBy(arrSportData, "sport_id");
console.log(groupedData);
Now you will have an array for each sports_id. In view populate it like
<div ng-repeat="(key, items) in groupedData">
<h4>Sports ID: {{key}}</h4>
<ul ng-repeat="item in items">
<li>User ID: {{item.user_id}}</li>
</ul>
</div>

Resources