How to map over an array, use an if clause, and filter out the bad data? - swift

I have an array of words, some may or may not have typos.
potentialWords = ["hello", "lkasjdf", "hunry"]
What I want to do is, return an array of all valid words, and also those words that were able to be autocorrected using a function I created correctWord. It returns an array of potential matches. so "hunry" might return ["hungry", "hurry"]. I will select the first index for the best guess.
Some words cannot be corrected however! e.g. "lkasjdf" will not find any corrections, but "hunry" will.
I was trying something like:
potentialWords.map {
if correctWord($0) != nil {
return correctWord($0)[0]
}
}
of course this will complain and say that I need a return outside the if clause. I can filter the list based on if the word can be corrected, and then map over the filtered list, re-checking which words need to be corrected, but this runs the correctWord function way too many times, and it is very sensitive.
I would like to be able to do one single pass through, and return an array of all valid words, and also corrected words.
P.S. I am calling correctWord twice in the map function for brevity, but of course I would assign correctWord($0) to a variable, and then if it isn't nil, take the first index and add it to the new list.

I think you're after flatMap. It's the same as map except it will also filter out any nil values.
potentialWords.flatMap { correctWord($0)?.first }

Related

Get elements of list beginning with specific letter

I have a list of words allWords and I am trying to create a new list from allWords containing only those words that begin with a specific letter currLetter. Looking at the docs, collection.map() seems like a great choice. However the statement below won't compile since .starts(with: ) returns a boolean.
targetWords = allWords.map { $0.starts(with: currLetter) }
Can anyone point me in the right direction?
While not described in the documentation of collection type instance methods, the solution is to use filter() instead of map():
targetWords = allWords.filter { $0.starts(with: currLetter) }

Hibernate Search 6 Scrollable returning results in a strange way

Since we upgraded from Hibernate Search 5.11 to Hibernate Search 6 we are having problems with Scrollable results
When we get a chunk of hits from the SearchScroll object each hit is stored in an Arrays.ArrayList
What we expected is that each chunk hits would be an ArrayList of say for example of type long
What we get is an ArrayList where where each hit is an Arrays.ArrayList with the Long value
Current code
SearchScroll scroll = searchSession
.search(scope)
.select(projectionArray)
.where(searchPredicate)
.sort(getSort(resultType))
.scroll(20);
Old code with Hibernate Search 5
FullTextQuery fullTextQuery = fullTextSession
.createFullTextQuery(query, resultType)
.setSort(getSort(resultType));
fullTextQuery.setProjection(fields);
ScrollableResults scrollableResults = fullTextQuery.scroll();
Any suggestions welcome
At worst we can loop through the results and convert the Arrays.ArrayList item to a long but cannot find a way to make that work either
The acual search results are correct just coming back in a different format that what we expect
Changing the code to
SearchScroll<Long> scroll = searchSession
.search(scope)
.select(projectionArray)
.where(searchPredicate)
.sort(getSort(resultType))
.scroll(20);
Makes no difference which seems to match the example in the docs
try ( SearchScroll<Book> scroll = searchSession.search(
Book.class )
.where( f -> f.matchAll() )
.scroll( 20 ) ) {
for ( SearchScrollResult<Book> chunk = scroll.next();
chunk.hasHits(); chunk = scroll.next() ) {
for ( Book hit : chunk.hits() ) {
// ... do something with the hits ...
}
totalHitCount = chunk.total().hitCount();
entityManager.flush();
entityManager.clear();
}
}
Not sure if the projection is what is causing the problem
Tested further if I remove the projection I get the results as an ArrayList of the object as expected so obviously I am doing something wrong with the use of projections in Hibernate Search 6
Without projection everything is good
With projection the results are Arrays.ArrayList
If I understand correctly, you are surprised that you get a List for each hit instead of just a Long.
First, I would recommend that you don't ignore raw type warnings.
I'll wager that your projectionArray is defined this way:
SearchProjection[] projectionArray = new SearchProjection[1];
That's wrong because you're using a "raw" type for SearchProjection, which basically disables all kinds of type-checking for all the code afterwards.
The correct way of defining that array is as follows:
SearchProjection<?>[] projectionArray = new SearchProjection<?>[1];
If you do that, then you'll get a compile-time error with the following code, telling you something like "cannot convert SearchScroll<List<?>> to SearchScroll<Long>":
SearchScroll<Long> scroll = searchSession
.search(scope)
.select(projectionArray)
.where(searchPredicate)
.sort(getSort(resultType))
.scroll(20);
Now, the reason you're getting a SearchScroll<List<?>> is you're passing an array of projections to .select(), so you're calling this method from SearchQuerySelectStep:
SearchQueryWhereStep<?, List<?>, LOS, ?> select(SearchProjection<?>... projections);
This method takes an array of projections as an argument, and (ultimately) returns a query whose hits are lists, with the results of requested projections in the same order as your array of projections.
You want to call that method instead:
<P> SearchQueryWhereStep<?, P, LOS, ?> select(SearchProjection<P> projection);
That method takes a single projection as an argument, and (ultimately) returns a query whose hits are directly the result of the requested projection.
To call that method, pass a single projection instead of an array of projections; then you will get the Long values you expect instead of Lists:
SearchProjection<Long> projection = ...;
SearchScroll<Long> scroll = searchSession
.search(scope)
.select(projection)
.where(searchPredicate)
.sort(getSort(resultType))
.scroll(20);

How to write to an Element in a Set?

With arrays you can use a subscript to access Array Elements directly. You can read or write to them. With Sets I am not sure of a way to write its Elements.
For example, if I access a set element matching a condition I'm only able to read the element. It is passed by copy and I can't therefore write to the original.
For example:
columns.first(
where: {
$0.header.last == Character(String(i))
}
)?.cells.append(value: addValue)
// ERROR: Cannot use mutating member on immutable value: function call returns immutable value
You can't just change things inside a set, because of how a (hash) set works. Changing them would possibly change their hash value, making the set into an invalid state.
Therefore, you would have to take the thing you want to change out of the set, change it, then put it back.
if var thing = columns.first(
where: {
$0.header.last == Character(String(i))
}) {
columns.remove(thing)
thing.cells.append(value: addValue)
columns.insert(thing)
}
If the == operator on Column doesn't care about cells (i.e. adding cells to a column doesn't suddenly make two originally equal columns unequal and vice versa), then you could use update instead:
if var thing = columns.first(
where: {
$0.header.last == Character(String(i))
}) {
thing.cells.append(value: addValue)
columns.update(thing)
}
As you can see, it's quite a lot of work, so maybe sets aren't a suitable data structure to use in this situation. Have you considered using an array instead? :)
private var _columns: [Column]
public var columns : [Column] {
get { _columns }
set { _columns = Array(Set(newValue)) }
// or any other way to remove duplicate as described here: https://stackoverflow.com/questions/25738817/removing-duplicate-elements-from-an-array-in-swift
}
You are getting the error because columns might be a set of struct. So columns.first will give you an immutable value. If you were to use a class, you will get a mutable result from columns.first and your code will work as expected.
Otherwise, you will have to do as explained by #Sweeper in his answer.

Array of observables where the output of each is the input of the next

I'm trying to use RxSwift to execute actions on multiple data sources. However, I have no idea how to accomplish the following.
I have an array of observabless where the output of each, should be the input of the next. So, I want to do something like, get the first observable, wait for the result and pass it to the next, all the way to the end of the array and return one final value.
Is that possible? Thanks in advance.
*** Update: Ok, I'll be more specific as requested.
The 'observables' I'm using in the array, are custom. I use a function that returns Observable.create { ... }. Inside the closure, I run an asynchronous operation that transforms the value and then send the result to the observer before completing. That resulting value, must pass to the next observable, and so on to the last observable in the array to get a final value.
The observables may send multiple values, but they must pass from one observable to the next like an assembly line.
It is difficult to know exactly what you are asking for, since Observables do not exactly have inputs but I think this is a common problem.
You may be looking for a combination of the concat or reduce operators, which allow you to accumulate data from the values emitted from an Observable. See ReactiveX's documentation for Mathematical and Aggregate Operators.
Hopefully this can get you started:
// "I have an array of observables..."
let one = Observable.deferred { Observable.just(1) }
let two = Observable.deferred { Observable.just(2) }
let observables = [one, two]
// "the output of each, should be the input of the next"
// this is problematic, because observables do not strictly have inputs.
let resultsFromEach = Observable.concat(observables)
resultsFromEach
.reduce(0) { result, next in
result + 1
}
.debug("result")
.subscribe()

Delete an element in a set by name

I have the following set:
class Element (var Name:String, var Description: String)
var MoreElement: Set[Element] = Set(E1, E2, E3, ...)
How do I delete an Element in a set MoreElement by name.
I found this solution:
MoreElement -= (MoreElement find (_.Name == "nameOfElementToRemove")).get
but I would not use the get, because if you does not find the item is thrown an exception, however I do not want no exception.
MoreElement = MoreElement filterNot (_.Name == "nameOfElementToRemove")
The direct answer to you question is to use filter, meaning something like:
moreElements = moreElements.filter( _.name != "nameOfElementToRemove")
Note this will scan the set. If you want a set indexed by name, you should really use a Map.
However, some caveats:
A set is a collection of unique elements. In order to compare elements in the set, it uses the contained type's equality operator. In your case, the Element class needs to define the 'equals' method (and hashCode) so the set can effectively compare instances.
In addition, you need to keep in mind that Set is an immutable class in Scala, so in your example you're really creating a new set, despite using an operator that appears to modify the existing set.
If you want a mutable set, you need to import scala.collection.mutable.Set.