Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to sort an array of string by similarity to specific key

Given is the following:

var theArray: [String] = ["uncool", "chill", "nifty", "precooled", "dandy", "cool"]

I want to sort the array by how similar the words are to the key word.

var keyWord: String = "cool"

The wanted result would be:

print// ["cool", "uncool", "precooled", ...] and then it does not matter anymore. But the words that are the key or contain it should be the very first objects.

My closest tryout so far has been:

let _theArray = entries.sorted { element1, element2 in

     return element1.contains(keyWord) && !element2.contains(keyWord)
}

But that results in uncool being the first item, then precooled and the most related item cool even comes after nifty .

What am I missing?

like image 232
David Seek Avatar asked Nov 06 '17 06:11

David Seek


People also ask

How do you sort objects based on keys?

To sort the keys of an object:Use the Object. keys() method to get an array of the object's keys. Call the sort() method on the array. Call the reduce() method to get an object with sorted keys.

How do you sort an array of strings?

To sort an array of strings in Java, we can use Arrays. sort() function.

How to sort an array in ascending order?

1 Using the Arrays.sort () Method. In Java, Arrays is the class defined in the java.util package that provides sort () method to sort an array in ascending order. 2 Sort String Array in Ascending Order or Alphabetical Order. ... 3 Sort String Array in Descending Order or Reverse Natural Order 4 Using the reverseOrder () Method. ...

How to sort an array by length in Python?

A simple solution is to write our own sort function that compares string lengths to decide which string should come first. Below is the implementation that uses Insertion Sort to sort the array.

How to sort an array of strings in lexicographic order?

The selection sort algorithm sorts an array by repeatedly finding the minimum element (considering ascending order) from unsorted part and putting it at the beginning // This code is contributed by hrithikgarg03188. Bubble Sort Approach: The basic approach to sort the given strings in lexicographic order is by using bubble sort.

Why can’t I sort an array of numbers correctly?

If you try to .sort () an array of numbers, they don’t seem to sort correctly: The default ECMAScript sort is alphabetical, so a little magic is needed to sort an array in numerical order. The reason is because .sort () defaults to sorting items alphabetically, in ascending order — and10 comes before 2 in an alphabetical list.


2 Answers

You can define your own similarity sorting method. Note that I have also added a hasPrefix priority over the ones which only contains the keyword which you can just remove if you don't want it:

var theArray = ["chill", "nifty", "precooled", "cooldaddy", "cool", "coolguy", "dandy", "uncool"]
let key = "cool"

let sorted = theArray.sorted {
    if $0 == key && $1 != key {
        return true
    }
    else if $0.hasPrefix(key) && !$1.hasPrefix(key)  {
        return true
    }
    else if !$0.hasPrefix(key) && $1.hasPrefix(key)  {
        return false
    }
    else if $0.hasPrefix(key) && $1.hasPrefix(key)
        && $0.count < $1.count  {
        return true
    }
    else if $0.contains(key) && !$1.contains(key) {
        return true
    }
    else if !$0.contains(key) && $1.contains(key) {
        return false
    }
    else if $0.contains(key) && $1.contains(key)
        && $0.count < $1.count {
        return true
    }
    return false
}

print(sorted)   // ["cool", "coolguy", "cooldaddy", "uncool", "precooled", "chill", "nifty", "dandy"]

You can also extend Sequence and create a sorted by key similarity method:

extension Sequence where Element: StringProtocol {
    func sorted<S>(by key: S) -> [Element] where S: StringProtocol {
        sorted {
            if $0 == key && $1 != key {
                return true
            }
            else if $0.hasPrefix(key) && !$1.hasPrefix(key)  {
                return true
            }
            else if !$0.hasPrefix(key) && $1.hasPrefix(key)  {
                return false
            }
            else if $0.hasPrefix(key) && $1.hasPrefix(key)
                && $0.count < $1.count  {
                return true
            }
            else if $0.contains(key) && !$1.contains(key) {
                return true
            }
            else if !$0.contains(key) && $1.contains(key) {
                return false
            }
            else if $0.contains(key) && $1.contains(key)
                && $0.count < $1.count {
                return true
            }
            return false
        }
    }
}

let sorted = theArray.sorted(by: key)  // "cool", "coolguy", "cooldaddy", "uncool", "precooled", "chill", "nifty", "dandy"]

And the mutating version as well:

extension MutableCollection where Element: StringProtocol, Self: RandomAccessCollection {
    mutating func sort<S>(by key: S) where S: StringProtocol {
        sort {
            if $0 == key && $1 != key {
                return true
            }
            else if $0.hasPrefix(key) && !$1.hasPrefix(key)  {
                return true
            }
            else if !$0.hasPrefix(key) && $1.hasPrefix(key)  {
                return false
            }
            else if $0.hasPrefix(key) && $1.hasPrefix(key)
                && $0.count < $1.count  {
                return true
            }
            else if $0.contains(key) && !$1.contains(key) {
                return true
            }
            else if !$0.contains(key) && $1.contains(key) {
                return false
            }
            else if $0.contains(key) && $1.contains(key)
                && $0.count < $1.count {
                return true
            }
            return false
        }
    }
}

like image 195
Leo Dabus Avatar answered Oct 19 '22 16:10

Leo Dabus


First you need a measure of how similar two strings are. Here's a simple example:

extension String {
    func equalityScore(with string: String) -> Double {
        if self == string {
            return 2     // the greatest equality score this method can give
        } else if self.contains(string) {
            return 1 + 1 / Double(self.count - string.count)   // contains our term, so the score will be between 1 and 2, depending on number of letters.
        } else {
            // you could of course have other criteria, like string.contains(self)
            return 1 / Double(abs(self.count - string.count))
        }
    }
}

Once you have that, you can use it to sort the array:

var theArray: [String] = ["uncool", "chill", "nifty", "precooled", "dandy", "cool"]

var compareString = "cool"

theArray.sort { lhs, rhs in
    return lhs.equalityScore(with: compareString) > rhs.equalityScore(with: compareString)
}

Result: ["cool", "uncool", "precooled", "chill", "nifty", "dandy"]

like image 39
Alex Wally Avatar answered Oct 19 '22 17:10

Alex Wally