Splitting up an array into a hash - arrays

I have an array of about 860 items, I would like to turn it into a hash, the array structure is (Key1, value 1, value2, value 3, Key2, value 1, value 2, value 3........... etc etc) except in real life it looks like (Peter, 150, 39, 345, John, 123, 450, 402, Mary, 145, 345, 506............). Original these are four arrays which I have transposed together, so I could reach the same end goal by starting from 4 independent arrays.
I would like
Hash {Peter=> [150, 39, 345], John=>[123,450,402], Mary => [145,345,506] etc etc
I feel there should be a nice neat way to do this, but it eludes me, possibly because I don't know enough ruby.

One more way:
l = ['Peter', 150, 39, 345, 'John', 123, 450, 402, 'Mary', 145, 345, 506]
Hash[*l.each_slice(4).flat_map { |a, *b| [a, b] }]
#=> {"Peter"=>[150, 39, 345],
# "John" =>[123, 450, 402],
# "Mary" =>[145, 345, 506]}
If you already have arrays, solution will be even more simple:
arrays = [array_1, array_2, array_3]
Hash[*arrays.flat_map {|a, *b| [a, b]} ]
#=> {"Peter"=>[150, 39, 345], "John"=>[123, 450, 402], "Mary"=>[145, 345, 506]}

arr = ['Peter', 150, 39, 345, 'John', 123, 450, 'Mary', 145, 345, 506, 222]
arr.slice_before {|a| String === a }.
each_with_object({}) {|(f,*rest), h| h[f] = rest}
#=> {"Peter"=>[150, 39, 345], "John"=>[123, 450], "Mary"=>[145, 345, 506, 222]}
Enumerable#slice_before made its debut in Ruby v2.3. One could alternatively use Enumerable#slice_when, which was new in v2.2.
Here's are a couple more ways that work with earlier versions of Ruby.
arr.chunk { |e| String === e }.
each_slice(2).
with_object({}) { |((_,(k)),(_,v)), h| h[k] = v }
and
a = []
enum = arr.to_enum
loop do
o = enum.next
(String === o) ? a << [o,[]] : a.last.last << o
end
Hash[a]

If you know that the data is always in sets of 4:
l = ['Peter', 150, 39, 345, 'John', 123, 450, 402, 'Mary', 145, 345, 506]
h = {}
l.each_slice(4).each do |chunk|
h[chunk[0]] = chunk[1..chunk.length]
end
Alternately, if you know the keys are always strings:
l = ['Peter', 150, 39, 345, 'John', 123, 450, 402, 'Mary', 145, 345, 506]
h = {}
current_key = ""
l.each do |item|
if item.is_a? String
current_key = item
h[item] = []
else
h[current_key] << item
end
end
Basically, you need to work out how to slice the array appropriately and then loop through.
Also if you've already got them as separate arrays you could easily just:
array_1 = ['Peter', 150, 39, 345]
array_2 = ['John', 123, 450, 402]
array_3 = ['Mary', 145, 345, 506]
h = {}
[array_1,array_2, array_3].each do |a|
h[a[0]] = a[1:a.length]
end
# One line version:
[array_1,array_2, array_3].map{|a| h[a[0]] = a[1..a.length]}
Which I'd argue is the cleanest method.

Original these are four arrays which I have transposed together, so I could reach the same end goal by starting from 4 independent arrays.
I assume you have something like this to start with:
a = ["Peter", "John", "Mary"]
b = [150, 123, 145]
c = [39, 450, 345]
d = [345, 402, 506]
You can combine the "value" arrays via zip:
values = b.zip(c, d)
#=> [[150, 39, 345], [123, 450, 402], [145, 345, 506]]
And add the "key" array via another zip:
a.zip(values)
#=> [["Peter", [150, 39, 345]], ["John", [123, 450, 402]], ["Mary", [145, 345, 506]]]
This is already the correct structure, so we can just call to_h:
a.zip(values).to_h
#=> {"Peter"=>[150, 39, 345], "John"=>[123, 450, 402], "Mary"=>[145, 345, 506]}
Of course you don't need the intermediate variable, a.zip(b.zip(c, d)).to_h returns the same result.

Try this
l = ['Peter', 150, 39, 345, 'John', 123, 450, 402, 'Mary', 145, 345, 506]
l.each_slice(4).to_a.inject({}){|acc, temp| acc[temp.first]=temp.last(3); acc}
#=>{"Peter"=>[150, 39, 345], "John"=>[123, 450, 402], "Mary"=>[145, 345, 506]}

Related

SwiftUI: I want to use the information in my array but I'm unable to find the right command

I'm very new to programming and I'm having trouble with the more complex aspects. I am trying to use a data set called playerCameCards and randomly add 5 of these to playerCards. I have managed to do that but I want to display name: of the last added element but I can't figure out how to do it. I'd be grateful for some help Below is my code.
import Foundation
import SwiftUI
class Cards: ObservableObject {
#Published var playerCards = [gameCards]()
#Published var playerGameCards = [
gameCards(id: 1, name: "name1", attack: 92, defence: 49, gameControl: 60, creativity: 72, legend: 4),
gameCards(id: 2, name: "name2", attack: 87, defence: 40, gameControl: 65, creativity: 80, legend: 2),
gameCards(id: 3, name: "name3", attack: 43, defence: 93, gameControl: 40, creativity: 45, legend: 3),
gameCards(id: 4, name: "name4", attack: 88, defence: 51, gameControl: 80, creativity: 92, legend: 5),
gameCards(id: 5, name: "name5", attack: 85, defence: 51, gameControl: 72, creativity: 81, legend: 3),
gameCards(id: 6, name: "name6", attack: 91, defence: 38, gameControl: 72, creativity: 89, legend: 5),
gameCards(id: 7, name: "name7", attack: 34, defence: 95, gameControl: 40, creativity: 50, legend: 5),
gameCards(id: 8, name: "name8", attack: 86, defence: 63, gameControl: 89, creativity: 84, legend: 4),
gameCards(id: 9, name: "name9", attack: 90, defence: 30, gameControl: 50, creativity: 83, legend: 5),
gameCards(id: 10, name: "name10", attack: 32, defence: 92, gameControl: 42, creativity: 32, legend: 4)
]
}
struct gameCards: Identifiable {
let id: Int
let name: String
let attack: Int
let defence: Int
let gameControl: Int
let creativity: Int
let legend: Int
}
import SwiftUI
struct ContentView: View {
#EnvironmentObject var cardData: Cards
#State var firstTime: Bool = false
#State var currentCard: String = "Back"
var body: some View {
ZStack{
VStack{
Spacer()
Text("Lets open your 1st cards").background(Color.white).font(.title)
Spacer()
Below is where I want show the name e.g. "name1" from the last element added to my playerCards array. I may want to make this an image in the future and use the ID: to display the corresponding image but I'm unable to access the types in my array
Text("I want to show the "name" of the element in the last array I've just used").frame(width: 200, height: 200).background(Color.blue)
Spacer()
Button(action:{
if self.cardData.playerCards.count <= 4{
self.cardData.playerCards.append(self.cardData.playerGameCards.randomElement()!)
print("\([self.cardData.playerCards.last])")
}
if self.cardData.playerCards.count > 4 {
self.firstTime = true
}
}){
if self.cardData.playerCards.count <= 4 {
Image("Click").renderingMode(.original).resizable().frame(width: 100, height: 100)
}
else{
NavigationLink(destination: ContentView()){Image("Continue").renderingMode(.original).resizable().frame(width: 100, height: 100)
}
}
}
}
}
}
}
This is how to embed a value in a String in Swift:
if self.cardData.playerCards.last != nil {
Text("I want to show the \(self.cardData.playerCards.last!.name) of the element in the last array I've just used")
}

Swift - Array filtering values in between two numbers

i have a small problem.
I have an array with a integer of value:
let array = [99, 42, 34, 19, 167, 30, 49, 39, 75, 175, 270, 540]
How do I get all the values between 19 and 167 for example? There has to be a better way than a for iteration through all the integer value ?
this is on swift. so i am looking for answer on swift.
thanks in advance
In Swift, you can still do this using filter
let array = [99, 42, 34, 19, 167, 30, 49, 39, 75, 175, 270, 540]
let newArray = array.filter{$0 > 19 && $0 < 167}
print(newArray)
You can use filter and the pattern matching operator ~= which is able to filter a range.
let array = [99, 42, 34, 19, 167, 30, 49, 39, 75, 175, 270, 540]
let range = 19...167
let filteredArray = array.filter{ range ~= $0 }
Consider that this operator does not filter the edges 19 and 167. The result is
// [99, 42, 34, 19, 167, 30, 49, 39, 75]
To exclude the edges write 20...168 or 20..<167, then the result is
// [99, 42, 34, 30, 49, 39, 75]
let filtered = array.filter { (20..<167).contains($0) }

Ruby array sort without changing position of nils

I am working with an array of arrays. I need to sort the rows, and the sort criteria is the value of each column (starting with the 2nd column) successively from left to right. Nil values must be pushed to the bottom. Once pushed to the bottom, I need to leave nil columns as they are without successive searches changing their position relative to each other. Starting with this array:
sort_array =
[[297, 100, 101, 235, 253, nil, nil, nil],
[286, 116, 116, 213, nil, nil, nil, nil],
[256, 105, 111, 212, 216, 264, nil, nil],
[276, 108, 111, 204, 207, 257, 259, 367],
[274, 66, 66, 120, 121, nil, 150, nil],
[298, 114, 117, 270, 270, nil, nil, nil],
[296, 127, 130, 259, 264, 324, 332, nil],
[283, 102, 106, 193, 199, 247, 248, 343]]
I iterate across the array with these lines of code:
(1..sort_array[0].size - 1).each do |i|
sort_array.sort_by! { |e| [e[i] ? 0 : 1, e[i]] }
end
The idea is to ignore the first column, but sort the second through nth column in ascending order pushing nil values to the bottom. This gives the following result:
=> [[283, 102, 106, 193, 199, 247, 248, 343],
[276, 108, 111, 204, 207, 257, 259, 367],
[256, 105, 111, 212, 216, 264, nil, nil],
[297, 100, 101, 235, 253, nil, nil, nil],
[296, 127, 130, 259, 264, 324, 332, nil],
[274, 66, 66, 120, 121, nil, 150, nil],
[298, 114, 117, 270, 270, nil, nil, nil],
[286, 116, 116, 213, nil, nil, nil, nil]]
This is close to what I need, but not quite right. The problem is that the nil values once pushed to the bottom don't stay put. For example, after sorting the column whose first value is 247, the array would have looked like this:
=> [[283, ... 247, ...]
[276, ... 257, ...]
[256, ... 264, ...]
[296, ... 324, ...]
[297, ... nil, ...]
[274, ... nil, ...]
[298, ... nil, ...]
[286, ... nil, ...]
But further sorting on columns to the right results in the nil values being reordered, which messes up the sort. Nil values need to stay put unless a cell to the right is non-nil, in which case it should be sorted as usual.
The table should look like this when the sort is finished:
=> [[283, 102, 106, 193, 199, 247, 248, 343],
[276, 108, 111, 204, 207, 257, 259, 367],
[274, 66, 66, 120, 121, nil, 150, nil],
[296, 127, 130, 259, 264, 324, 332, nil],
[256, 105, 111, 212, 216, 264, nil, nil],
[297, 100, 101, 235, 253, nil, nil, nil],
[298, 114, 117, 270, 270, nil, nil, nil],
[286, 116, 116, 213, nil, nil, nil, nil]]
Could someone please help me write a method that will achieve this result?
It may be helpful to see the real-life example of what I am trying to achieve. Here is a link to a spreadsheet:
https://github.com/SplitTime/OpenSplitTime/blob/master/hardrock2015test.xlsx
Each row is a runner effort. Runners are ranked by final finish time of course. But those who do not finish are ranked by how far they made it and what time they made it to their last waypoint.
Though the linked spreadsheet does not show it, the sort also needs to deal with nil values with time data to the right, representing times that didn't get recorded for whatever reason.
I think that you want to sort the rows by its 2nd to last elements in reverse order (with nil being treated as Infinity):
arrays = [[297, 100, 101, 235, 253, nil, nil, nil],
[286, 116, 116, 213, nil, nil, nil, nil],
[256, 105, 111, 212, 216, 264, nil, nil],
[276, 108, 111, 204, 207, 257, 259, 367],
[274, 66, 66, 120, 121, nil, 150, nil],
[298, 114, 117, 270, 270, nil, nil, nil],
[296, 127, 130, 259, 264, 324, 332, nil],
[283, 102, 106, 193, 199, 247, 248, 343]]
arrays.sort_by { |a| a[1..-1].reverse.map { |e| e || Float::INFINITY } }
#=> [[283, 102, 106, 193, 199, 247, 248, 343],
# [276, 108, 111, 204, 207, 257, 259, 367],
# [274, 66, 66, 120, 121, nil, 150, nil],
# [296, 127, 130, 259, 264, 324, 332, nil],
# [256, 105, 111, 212, 216, 264, nil, nil],
# [297, 100, 101, 235, 253, nil, nil, nil],
# [298, 114, 117, 270, 270, nil, nil, nil],
# [286, 116, 116, 213, nil, nil, nil, nil]]
At least, this produces the expected result.
Since the first row consists of keys, you could take advantage of Ruby's array decomposition abilities and use |_, *vs| vs.reverse... instead of |a| a[1..-1].reverse.... Here, _ refers to the (unused) key and *vs collects the remaining elements.

How to decrease counter in for loop?

I'm using the array shuffle function from here: http://iosdevelopertips.com/swift-code/swift-shuffle-array-type.html.
On this line:
for var index = array.count - 1; index > 0; index -= 1
in the code below
func shuffleArray<T>( arrayparam: Array<T>) -> Array<T>
{
var array = arrayparam
for var index = array.count - 1; index > 0; index -= 1
{
// Random int from 0 to index-1
let j = Int(arc4random_uniform(UInt32(index-1)))
// Swap two array elements
// Notice '&' required as swap uses 'inout' parameters
swap(&array[index], &array[j])
}
return array
}
Swift throws this warning:
C-style for statement is deprecated and will be removed in a future
version of Swift
There isn't any recommendation of what should be used here. Any ideas what should replace it?
take a look at http://bjmiller.me/post/137624096422/on-c-style-for-loops-removed-from-swift-3
only decrease by 1:
for i in (0...n).reverse() {
}
or
decrease by more steps:
for i in someNum.stride(through: 0, by: -2) {
}
More info: there are two versions for stride: through and to. Difference is <= and >= for through, while < and > for to, depending on what you need.
func shuffle<T>(array: Array<T>) -> Array<T> {
var result = array
for index in array.indices.reverse() {
// generate random swapIndex and add a where clause
// to make sure it is not equal to index before swaping
guard
case let swapIndex = Int(arc4random_uniform(UInt32(array.count - index))) + index
where index != swapIndex
else { continue }
swap(&result[index], &result[swapIndex])
}
return result
}
var arrInt = Array(1...100)
shuffle(arrInt) // [28, 19, 25, 53, 35, 60, 14, 62, 34, 15, 81, 50, 59, 40, 89, 30, 2, 54, 27, 9, 82, 21, 11, 67, 84, 75, 44, 97, 66, 83, 36, 20, 26, 1, 76, 77, 8, 13, 72, 65, 64, 80, 88, 29, 98, 37, 33, 70, 52, 93, 100, 31, 4, 95, 45, 49, 61, 71, 24, 16, 12, 99, 94, 86, 46, 69, 63, 22, 48, 58, 51, 18, 43, 87, 41, 6, 92, 10, 38, 23, 68, 85, 42, 32, 55, 78, 56, 79, 3, 47, 39, 57, 90, 17, 5, 73, 7, 91, 74, 96]
for var index = array.count - 1; index > 0; index -= 1
Use a reverse range. Form the range and reverse it:
for index in (1..<array.count).reverse
However, as discussed in my answer here, there's a nicer way; I provide a >>> operator so you can say
for index in array.count>>>1
why don't you try:
for var index = array.count - 1; index > 0; index =index-1 ;

Creating a Dictionary: Assigning an Array of Ints as Keys to another array

I have an array of Arrays and an Array of Integers and want to create a dictionary with the Integers Array elements as keys to elements in the Array of Array.
I have tried a number of iteration methods without much luck. Any thoughts or ideas?
var populationArray = [[98, 8, 45, 34, 56], [9, 13, 65, 4, 90], [24, 5, 4, 56, 88], [3, 55, 22, 19, 10], [8, 33, 26, 93, 16], [31, 38, 92, 70, 36], [9, 39, 15, 14, 66]]
var IntegerKeys = [17, 41, 10, 34, 5, 85, 87]
var Dictionary : [Int: [Int]] = [:]
Try this:
var populationArray = [[98, 8, 45, 34, 56], [9, 13, 65, 4, 90], [24, 5, 4, 56, 88], [3, 55, 22, 19, 10], [8, 33, 26, 93, 16], [31, 38, 92, 70, 36], [9, 39, 15, 14, 66]]
var IntegerKeys = [17, 41, 10, 34, 5, 85, 87]
var Dictionary = [Int: [Int]]()
// Swift 2.0
for (index, key) in IntegerKeys.enumerate() {
Dictionary[key] = populationArray[index]
}
// Swift 1.2
for (index, key) in enumerate(IntegerKeys) {
Dictionary[key] = populationArray[index]
}
#ZoffDino's answer does work, however it would crash if the populationArray would contain less elements than integerKeys. I'm proposing a method that doesn't have this flaw:
var populationArray = [[98, 8, 45, 34, 56], [9, 13, 65, 4, 90], [24, 5, 4, 56, 88], [3, 55, 22, 19, 10], [8, 33, 26, 93, 16], [31, 38, 92, 70, 36], [9, 39, 15, 14, 66]]
var IntegerKeys = [17, 41, 10, 34, 5, 85, 87]
var dictionary : [Int: [Int]] = [:]
for (key, value) in zip(IntegerKeys, populationArray) {
dictionary[key] = value
}

Resources