Group slice by subarray values in Go - go

I have an array of subarrays in the following format
Array
(
[0] => Array
(
[unit_id] => 6504
[assignment_name] => Grade assignment
[assignment_description] =>
[assignment_total_score] => 10
[unit_type_name] => Homework
[is_graded] => 1
[standard_id] => 1219
[scoring_type] => score
[attempt_score] => 8
[unit_duedate] => 2016-02-10 09:00:00
[standard] => Array
(
[0] => stdClass Object
(
[unit_id] => 6504
[is_formal] => 1
[assignment_name] => Grade assignment
[assignment_description] =>
[standard_id] => 1220
[standard_name] => 9-10.RL.3
[standard_description] => Analyze how complex characters (e.g., those with multiple or conflicting motivations) develop over the course of a
)
)
)
[1] => Array
(
[unit_id] => 8584
[assignment_name] => Sine and Cosecant Graphs
[assignment_description] => Define the sine and cosecant graphs using a unit circle
[assignment_total_score] => 15
[unit_type_name] => Paper
[scoring_type] => score
[attempt_score] => 0
[unit_duedate] => 2016-04-29 09:00:00
[standard] => Array
(
[0] => stdClass Object
(
[unit_id] => 8584
[is_formal] => 1
[assignment_name] => Sine and Cosecant Graphs
[assignment_description] => Define the sine and cosecant graphs using a unit circle
[assignment_total_score] => 15
[standard_id] => 82790
[standard_name] => 9-10.RL.7
)
)
[2] => Array
(
[unit_id] => 11611
[assignment_name] => Adding 5 + 3 + 6
[assignment_description] =>
[assignment_total_score] => 10
[unit_type_name] => Homework
[standard_id] => 82772
[scoring_type] => score
[attempt_score] => 0
[unit_duedate] => 2016-08-23 19:00:00
[standard] => Array
(
[0] => stdClass Object
(
[unit_id] => 11611
[is_formal] => 1
[assignment_name] => Adding 5 + 3 + 6
[assignment_description] =>
[assignment_total_score] => 10
[standard_id] => 82772
[standard_name] => 9-10.RL.1
)
)
)
)
And I would like to group it into a new slice based on the unit_type_name field in each subarray.
How can I group the slice by unit_type_name? Is there any native Go functions are available to do this?
if I for loop the above then I will get a duplicate one, how can I avoid that?

I do not think Go has a built-in functionality to help you do that (I may be wrong). My Assumption is that the PHP array will be converted to a JSON object. I managed to get the code below to help you sort your array (In JSON format) a based on the unit_type_name
I created two structs which have JSON values similar to what the array keys would be
//StandardType ...
type StandardType struct {
UnitID int `json:"unit_id"`
IsFormal int `json:"is_formal"`
AssignmentName string `json:"assignment_name"`
AssignmentDescription string `json:"assignment_description"`
StandardID int `json:"standard_id"`
StandardName string `json:"standard_name"`
StandardDescription string `json:"standard_description"`
}
//AutoGenerated ...
type AutoGenerated struct {
UnitID int `json:"unit_id"`
AssignmentName string `json:"assignment_name"`
AssignmentDescription string `json:"assignment_description"`
AssignmentTotalScore int `json:"assignment_total_score"`
UnitTypeName string `json:"unit_type_name"`
IsGraded int `json:"is_graded"`
StandardID int `json:"standard_id"`
ScoringType string `json:"scoring_type"`
AttemptScore int `json:"attempt_score"`
UnitDuedate string `json:"unit_duedate"`
Standard []StandardType `json:"standard"`
}
var jsonData = ``
func main() {
m := []AutoGenerated{}
err := json.Unmarshal([]byte(jsonData), &m)
if err != nil {
panic(err)
}
I created a map to hold the unit_type_name keys
sliceKeys := make(map[string]string)
I created also map to hold the arrays that have similar unit_type_name keys in an AutoGenerated array
groupedSlices := make(map[string][]AutoGenerated)
Then I loop through the decoded JSON string searching for the unit_type_name
for i := range m {
If a unit_type_name already exists in the key slice I add the array item to the group slice
if _, ok := sliceKeys[m[i].UnitTypeName]; ok {
autogenerated := groupedSlices[m[i].UnitTypeName]
autogenerated = append(autogenerated, m[i])
groupedSlices[m[i].UnitTypeName] = autogenerated
} else {
Else I create a new array key and add the item to it
sliceKeys[m[i].UnitTypeName] = m[i].UnitTypeName
autogenerated := []AutoGenerated{}
autogenerated = append(autogenerated, m[i])
groupedSlices[m[i].UnitTypeName] = autogenerated
}
}
fmt.Println(sliceKeys)
fmt.Println(groupedSlices)
}
input:
[{"unit_id": 6504,"assignment_name": "Grade assignment","assignment_description": "","assignment_total_score": 10,"unit_type_name": "Homework","is_graded": 1,"standard_id": 1219,
"scoring_type": "score","attempt_score": 8,"unit_duedate": "2016-02-10 09:00:00",
"standard": [{"unit_id": 6504,"is_formal": 1,"assignment_name": "Grade assignment","assignment_description": "",
"standard_id": 1220,"standard_name": "9-10.RL.3","standard_description": "Analyze how complex characters (e.g., those with multiple or conflicting motivations) develop over the course of a "
}]},{"unit_id": 6504,"assignment_name": "Grade assignment","assignment_description": "","assignment_total_score": 10,
"unit_type_name": "Paper","is_graded": 1,"standard_id": 1219,"scoring_type": "score","attempt_score": 8,"unit_duedate": "2016-02-10 09:00:00","standard": [{"unit_id": 6504,"is_formal": 1,"assignment_name": "Grade assignment","assignment_description": "","standard_id": 1220,"standard_name": "9-10.RL.3","standard_description": "Analyze how complex characters (e.g., those with multiple or conflicting motivations) develop over the course of a "}]},{
"unit_id": 6504,"assignment_name": "Grade assignment","assignment_description": "",
"assignment_total_score": 10,"unit_type_name": "Aything else","is_graded": 1,"standard_id": 1219,
"scoring_type": "score","attempt_score": 8,"unit_duedate": "2016-02-10 09:00:00","standard": [{
"unit_id": 6504,"is_formal": 1,"assignment_name": "Grade assignment","assignment_description": "","standard_id": 1220,
"standard_name": "9-10.RL.3","standard_description": "Analyze how complex characters (e.g., those with multiple or conflicting motivations) develop over the course of a "}]}]
output:
map[Homework:Homework Paper:Paper Aything else:Aything else]
map[
Homework:[
{6504 Grade assignment 10 Homework 1 1219 score 8 2016-02-10 09:00:00 [{6504 1 Grade assignment 1220 9-10.RL.3 Analyze how complex characters (e.g., those with multiple or conflicting motivations) develop over the course of a }]}
]
Paper:[
{6504 Grade assignment 10 Paper 1 1219 score 8 2016-02-10 09:00:00 [{6504 1 Grade assignment 1220 9-10.RL.3 Analyze how complex characters (e.g., those with multiple or conflicting motivations) develop over the course of a }]}
]
Aything else:[
{6504 Grade assignment 10 Aything else 1 1219 score 8 2016-02-10 09:00:00 [{6504 1 Grade assignment 1220 9-10.RL.3 Analyze how complex characters (e.g., those with multiple or conflicting motivations) develop over the course of a }]}]
]

Here is a general GroupBy function I wrote:
// Created by BaiJiFeiLong#gmail.com at 2021/8/27 10:51
package main
import (
"fmt"
"reflect"
"strconv"
)
func GroupBy(arr interface{}, groupFunc interface{}) interface{} {
groupMap := reflect.MakeMap(reflect.MapOf(reflect.TypeOf(groupFunc).Out(0), reflect.TypeOf(arr)))
for i := 0; i < reflect.ValueOf(arr).Len(); i++ {
groupPivot := reflect.ValueOf(groupFunc).Call([]reflect.Value{reflect.ValueOf(arr).Index(i)})[0]
if !groupMap.MapIndex(groupPivot).IsValid() {
groupMap.SetMapIndex(groupPivot, reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(arr).Elem()), 0, 0))
}
groupMap.SetMapIndex(groupPivot, reflect.Append(groupMap.MapIndex(groupPivot), reflect.ValueOf(arr).Index(i)))
}
return groupMap.Interface()
}
func main() {
fmt.Println(GroupBy([]int{1, 22, 3, 44, 555}, func(value int) string {
return strconv.Itoa(len(strconv.Itoa(value)))
}))
}
The output:
map[1:[1 3] 2:[22 44] 3:[555]]
For your question, it shoube be some code like:
group := GroupBy(items, func(item Item) string {
return item.UnitTypeName
}).(map[string][]Item)

Related

Go command to get list of keywords or reserved words?

In python they have provided command to list the keywords in it by
>>> import keyword
>>> print(keyword.kwlist)
['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
is there a similar way in go?
The token package has a function IsKeyword. The code checks the existence of the string in
var keywords map[string]Token
This var is not exported, unfortunately. But you can build the same map like it is done in the standard lib:
func init() {
keywords = make(map[string]Token)
for i := keyword_beg + 1; i < keyword_end; i++ {
keywords[tokens[i]] = i
}
}
keyword_beg and keyword_end are constant values that mark beginning and end of the keyword constants. These also are not exported, but you can still use the value (resolved to 60 and 86).
So you convert int values from 60 to 86 to token.Token and then call Token.String. Like this
tokens := make([]token.Token, 0)
for i := 61; i < 86; i++ {
tokens = append(tokens, token.Token(i))
}
fmt.Println(tokens)

How can I validate the number of digits when converting from a string by chaining matches?

I just completed the Programming a Guessing Game chapter of The Rust Programming Language. I now want to add validation for the number of digits but couldn't find a pleasing way to do it.
I am able to achieve it by replacing
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("Please type a number.");
continue;
},
};
with
let guess = guess.trim();
if guess.parse::<u32>().is_err() || guess.chars().count() > 2 {
println!("Please type a number from 1 to 100.");
continue;
}
let guess: u32 = match guess.parse() {
Ok(num) => num,
Err(_) => continue, // this will never happen
};
The way I would prefer to do this is by somehow chaining matches, but I couldn't figure it out.
You don't need to chain match. You just need a different pattern:
let guess: u32 = match guess.trim().parse() {
Ok(num # 1...100) => num,
_ => {
println!("Please type a number within 1-99.");
continue;
},
};
This pattern means "If it's Ok and it has something bigger than 0 but smaller than 100, return its content, otherwise print message and continue."
You can chain matches like this:
let guess: u32 = match guess.trim().parse() {
Ok(num) => match guess.chars().count() {
1 | 2 => num,
_ => {
println!("Please type a number from 1 to 100.");
continue;
}
},
Err(_) => {
println!("Please type a number.");
continue;
},
};
However, checking the string length is not what you really want. For example "00023" parses to "23" but has a strength length of 5.
Since you have access to num in this context, you can match on its value directly!
// ...
Ok(num) => match num {
1..=100 => num,
_ => {
println!("Please type a number from 1 to 100.");
continue;
}
},
// ...

RXJS How convert observable<T>[] to observable<T[]>?

How convert array of observable to observable of array?
func f1: string -> Observable<T>
o: Observable<string[]>
o.map( x => x.map( y=> f1(y)))
.mergeAll()
return is Observable<T>[].
How can i get it ?
Correct solution :
o.mergeMap( x=> Observable.from(
x.map( y => f1(y) ).mergeAll().toArray()
)
)
or if not exist mergeMap():
o.map( x=> Observable.from(
x.map( y => f1(y) ).mergeAll().toArray()
)
).mergeAll()

What's the closest I can get to discriminating an enum by a char?

I've written this question out many times, and have finally realized that my biggest problem is that I don't know how I want to represent this data, and that's making it really hard to reason about the rest of the code.
The way the data is represented in Python:
class LSP():
C_MASK_MAP={
"A":"Ch A",
"B":"Ch B",
"C":"Ch C",
"D":"Ch D",
"T":"Tmpr",
"Y":"Batt",
"L":"Acc"
}
ADC_CHANS= (
"Ch A",
"Ch B",
"Ch C",
"Ch D",
"Tmpr",
"Batt"
)
ADC_MAJORS = (
"Ch A",
"Ch B",
"Ch C",
)
My imaginary Rust code (I realize the names will need updating but are the same here for clarity):
enum C_MASK_MAP {
Ch_A = 'A',
Ch_B = 'B',
Ch_C = 'C',
Ch_D = 'D',
Tmpr = 'T',
Batt = 'Y',
Acc = 'L'
}
//...
let ADC_CHANS = [
C_MASK_MAP::Ch_A,
C_MASK_MAP::Ch_B,
C_MASK_MAP::Ch_C,
C_MASK_MAP::Ch_D,
C_MASK_MAP::Tmpr,
C_MASK_MAP::Batt
];
ADC_MAJORS = [
C_MASK_MAP::Ch_A,
C_MASK_MAP::Ch_B,
C_MASK_MAP::Ch_C,
];
I've considered making C_MASK_MAP a HashMap<char, &'static str>, but then I ran into a huge mess trying not to make a million copies of the strs everywhere and dealing with lifetimes while avoiding making Strings, and the syntactic mess that is a reference to a static str (&&'static str or something).
I think there'd be a real benefit to being able to use an enum (or similar) because the values wouldn't be as big and are more easily interchanged C_MASK_MAP.get(key).expect("invalid key") vs just casting.
Your strings are sentinel values; this is a common pattern in Python, but is not how things should be done in Rust: enums are what such things should be: you’re encoding the legal values in the type system.
You could end up with something like this:
#[derive(Clone, Copy)]
#[repr(u8)]
pub enum Mask {
ChA = b'A',
ChB = b'B',
ChC = b'C',
ChD = b'D',
Tmpr = b'T',
Batt = b'Y',
Acc = b'L',
}
// e.g. Mask::ChA.into() == 'A'
impl Into<char> for Mask {
fn into(self) -> char {
self as u8 as char
}
}
impl Mask {
// e.g. Mask::from('A') == Ok(Mask::ChA)
pub fn from(c: char) -> Result<Mask, ()> {
match c {
'A' => Ok(Mask::ChA),
'B' => Ok(Mask::ChB),
'C' => Ok(Mask::ChC),
'D' => Ok(Mask::ChD),
'T' => Ok(Mask::Tmpr),
'Y' => Ok(Mask::Batt),
'L' => Ok(Mask::Acc),
_ => Err(()),
}
}
// e.g. Mask::ChA.is_chan() == true
pub fn is_chan(&self) -> bool {
match *self {
Mask::ChA | Mask::ChB | Mask::ChC | Mask::ChD | Mask::Tmpr | Mask::Batt => true,
Mask::Acc => false,
}
}
// e.g. Mask::ChD.is_major() == false
pub fn is_major(&self) -> bool {
match *self {
Mask::ChA | Mask::ChB | Mask::ChC => true,
Mask::ChD | Mask::Tmpr | Mask::Batt | Mask::Acc => false,
}
}
}
If you wanted you could implement std::str::FromStr for Mask as well, which would allow "A".parse() == Ok(Mask::ChA):
impl FromStr for Mask {
type Err = ();
fn from_str(s: &str) -> Result<Mask, ()> {
match s {
"A" => Ok(Mask::ChA),
"B" => Ok(Mask::ChB),
"C" => Ok(Mask::ChC),
"D" => Ok(Mask::ChD),
"T" => Ok(Mask::Tmpr),
"Y" => Ok(Mask::Batt),
"L" => Ok(Mask::Acc),
_ => Err(()),
}
}
}
I suspect that is_chan et al. may be more suitable than ADC_CHANS et al., but if you do actually need them, they work fine (you could do [Mask; 6] too, but if you need to add new elements it’d change the type which is an API compatibility break if public):
pub static ADC_CHANS: &'static [Mask] = &[
Mask::ChA,
Mask::ChB,
Mask::ChC,
Mask::ChD,
Mask::Tmpr,
Mask::Batt,
];
pub static ADC_MAJORS: &'static [Mask] = &[
Mask::ChA,
Mask::ChB,
Mask::ChC,
];
Copying a &'static str (i.e. copying the reference only) has no cost. A deep copy of the string would be a clone and would be typed as a String.
If &'static str is too verbose for you, you can always define a type alias.
type Str = &'static str;
HashMap<char, &'static str> corresponds nicely to your original map. However, if you don't need the full range of char for the key and you don't actually need to have the value typed as a char anywhere besides indexing the map, you should use an enum instead, as that will restrict the legal values that can be used as keys.

How to compose function to applicatives with scalaz

While learning Scalaz 6, I'm trying to write type-safe readers returning validations. Here are my new types:
type ValidReader[S,X] = (S) => Validation[NonEmptyList[String],X]
type MapReader[X] = ValidReader[Map[String,String],X]
and I have two functions creating map-readers for ints and strings (*):
def readInt( k: String ): MapReader[Int] = ...
def readString( k: String ): MapReader[String] = ...
Given the following map:
val data = Map( "name" -> "Paul", "age" -> "8" )
I can write two readers to retrieve the name and age:
val name = readString( "name" )
val age = readInt( "age" )
println( name(data) ) //=> Success("Paul")
println( age(data) ) //=> Success(8)
Everything works fine, but now I want to compose both readers to build a Boy instance:
case class Boy( name: String, age: Int )
My best take is:
val boy = ( name |#| age ) {
(n,a) => ( n |#| a ) { Boy(_,_) }
}
println( boy(data) ) //=> Success(Boy(Paul,8))
It works as expected, but the expression is awkward with two levels of applicative builders. Is there a way, to get the following syntax to work ?
val boy = ( name |#| age ) { Boy(_,_) }
(*) Full and runnable implementation in: https://gist.github.com/1891147
Update: Here is the compiler error message that I get when trying the line above or Daniel suggestion:
[error] ***/MapReader.scala:114: type mismatch;
[error] found : scalaz.Validation[scalaz.NonEmptyList[String],String]
[error] required: String
[error] val boy = ( name |#| age ) { Boy(_,_) }
[error] ^
How about this?
val boy = (name |#| age) {
(Boy.apply _).lift[({type V[X]=ValidationNEL[String,X]})#V]
}
or using a type alias:
type VNELStr[X] = ValidationNEL[String,X]
val boy = (name |#| age) apply (Boy(_, _)).lift[VNELStr]
This is based on the following error message at the console:
scala> name |#| age apply Boy.apply
<console>:22: error: type mismatch;
found : (String, Int) => MapReader.Boy
required: (scalaz.Validation[scalaz.NonEmptyList[String],String],
scalaz.Validation[scalaz.NonEmptyList[String],Int]) => ?
So I just lifted Boy.apply to take the required type.
Note that since Reader and Validation (with a semigroup E) are both Applicative, their composition is also Applicative. Using scalaz 7 this can be expressed as:
import scalaz.Reader
import scalaz.Reader.{apply => toReader}
import scalaz.{Validation, ValidationNEL, Applicative, Kleisli, NonEmptyList}
//type IntReader[A] = Reader[Int, A] // has some ambigous implicit resolution problem
type IntReader[A] = Kleisli[scalaz.IdInstances#Id, Int, A]
type ValNEL[A] = ValidationNEL[Throwable, A]
val app = Applicative[IntReader].compose[ValNEL]
Now we can use a single |#| operation on the composed Applicative:
val f1 = toReader((x: Int) => Validation.success[NonEmptyList[Throwable], String](x.toString))
val f2 = toReader((x: Int) => Validation.success[NonEmptyList[Throwable], String]((x+1).toString))
val f3 = app.map2(f1, f2)(_ + ":" + _)
f3.run(5) should be_==(Validation.success("5:6"))

Resources