How to get unique entries from below JSON in Go. In the output I should get unique key-value pairs.Here in the input there are 2 same keys type and different values for them. So in the output I should get one key as type and combined values for them. And If there are duplicate key-value pairs one pair needs to be removed.
This is my input
{
"attributes": [
{
"name": "name",
"values": [
"name"
]
},
{
"name": "Type",
"values": [
"type1",
"type2"
]
},
{
"name": "Id",
"values": [
"id"
]
},
{
"name": "Type",
"values": [
"type3",
"type4"
]
}
]
}
And output should look like this
{
"attributes": [
{
"name": "name",
"values": [
"name"
]
},
{
"name": "Type",
"values": [
"type1",
"type2",
"type3",
"type4"
]
},
{
"name": "Id",
"values": [
"id"
]
}
]
}
Here is the code I tried to add uniqueness to the name field. But I need values field uniqueness as well
var res []models.Attributes
var attributes []models.Attributes
keys := make(map[string]bool)
for _, item := range attributes {
if _, value := keys[item.Name]; !value {
keys[item.Name] = true
res = append(res, item)
}
}
return res
You are close. Use a map of maps where the outer map represents an attribute and the keys of the inner map are the unique values for that attribute:
m := make(map[string]map[string]bool)
Loop through the data creating maps as needed:
for _, item := range attributes {
values := m[item.Name]
if values == nil {
values = make(map[string]true)
m[item.Name] = values
}
for _, value := range item.Values {
values[value] = true
}
}
Recreate the slice of attributes from the map:
attributes = attributes[:0] // overwrite the attributes
for name, values := range m {
attribute := Attribute{Name: name}
for value := range values {
attribute.Values = append(attribute.Values, value)
}
attributes = append(attributes, attribute)
}
View the code in action on the playground.
Related
I need to transform array to array with some extra logic:
Map field name in array if it exists in the mapping object, if not process it as it is in the source
Sum up values of objects with the same name
Remove objects with zero value
for example here is the source json:
{
"additive": [
{
"name": "field-1",
"volume": "10"
},
{
"name": "field-2",
"volume": "10"
},
{
"name": "field-3",
"volume": "0"
},
{
"name": "field-4",
"volume": "5"
}
]
}
object with mapping config(field-1 and field-2 is mapped to the same value):
{
"field-1": "field-1-mapped",
"field-2": "field-1-mapped",
"field-3": "field-3-mapped"
}
and this is the result that I need to have
{
"chemicals": [
{
"name": "field-1-mapped",
"value": 20
},
{
"name": "field-4",
"value": 5
}
]
}
as you can see field-1 and field-2 is mapped to field-1-mapped so the values are summed up, field-3 has 0 value so it is removed and field-4 is passed as it is because it's missing in the mapping.
so my question is: is it possible to make it with JSONata?
I have tried to make it work but I stuck with this lookup function that doesn't return default value when name is missing in mapping:
{
"chemicals": additive # $additive #$.{
"name": $res := $lookup({
"field-1": "field-1-mapped",
"field-2": "field-1-mapped",
"field-3": "field-3-mapped"
}, $additive.name)[ $res ? $res : $additive.name],
"amount": $number($additive.volume),
} [amount>0]
}
Probably easiest to break it down into steps as follows:
(
/* define lookup table */
$table := {
"field-1": "field-1-mapped",
"field-2": "field-1-mapped",
"field-3": "field-3-mapped"
};
/* substitute the name; if it's not in the table, just use the name */
$mapped := additive.{
"name": [$lookup($table, name), name][0],
"volume": $number(volume)
};
/* group by name, and aggregate the volumes */
$grouped := $mapped[volume > 0]{name: $sum(volume)};
/* convert back to array */
{
"chemicals": $each($grouped, function($v, $n) {{
"name": $n,
"volume": $v
}})
}
)
See https://try.jsonata.org/0BWeRcRoZ
Hi I have json data which I Unmarshal to machines slice . Now I am looking to copy/append each cluster information, hostname from machines slice struct to Cluster Struct []Cluster and try to populate different values in that struct.
Each machine record has an associated serviceName . I am Looking for the out json format in the desired output below where service_name ,required, vars, value are only passed once even though they are associated with each json record when passed from machine slice.
current Code :
https://go.dev/play/p/6zVRIaLIgdN
Desired output :
{
"cluster_name": "dev",
"services": [
{
"service_name": "serviceA",
"required" : true,
"vars": {
"common_vars": {
"user": "custom-user",
"group": "custom-group"
}
},
"hosts": [
{
"host_name": "host1",
"vars": {
"common_vars" :{
"id": 1
}
}
},
{
"host_name": "host2",
"vars": {
"common_vars":{
"id": 2
}
}
}
]
},
{
"service_name": "serviceB",
"required" : false
.....
}
}
]
}
Current OutPut:
where the ServiceName is repeated with every machine name , I want it to have service_name once in the slice as above output
"cluster_name": "dev",
"services": [
{
"service_name": "serviceA",
"required": true,
"hosts": [
{
"host_name": "Machine-1",
"vars": {
"common_vars": {
"id": "1"
},
"custom_listeners": {}
}
}
],
"vars": {
"custom_listeners": {}
}
},
{
**"service_name": "serviceA"**,
"required": true,
"hosts": [
{
"host_name": "Machine-2",
"vars": {
"common_vars": {
"id": "2"
},
"custom_listeners": {}
}
}
],
"vars": {
"custom_listeners": {}
}
}
]
}
You have to implement some logic for merging service records with same name.
map[<ServiceName>]<Service> could be used to avoid iterating through slice of services every time.
m := map[string]*Service{}
for i := range machines {
s, found := m[machines[i].Servicename]
if !found {
s = &Service{ServiceName: machines[i].Servicename, Required: true}
m[machines[i].Servicename] = s
}
s.Hosts = append(s.Hosts, Host{HostName: machines[i].Hostname, Vars: Var{CommonVars: map[string]interface{}{"id": machines[i].ID}}})
}
for _, s := range m {
cService.Services = append(cService.Services, *s)
}
I need to create a JSON array in order to split it into several jobs with Nifi.
The array needs to be created based on an existing array inside the JSON.
Can't figure out how to dynamically create a reference to another object in the JSON. I want the reference "#(2,#)" to work, but this is not supported.
INPUT
{
"name": "Loki",
"id": "1234",
"loc": "Utgard",
"age": "unknown",
"listitems": [
"name",
"id"
]
}
SPEC (that doesn't work):
[
{
"operation": "shift",
"spec": {
// Loop all listitems
"listitems": {
"*": {
// Get the value of the current item and push to processlist.type array
"#": "processlist[#2].type",
// Here is the problem, I need to get the "top level" value for the current value/key
"#(2,#)": "processlist[#2].value"
}
}
}
}
]
Expected output:
{
"processlist" : [
{
"type" : "name",
"value" : "Loki"
}, {
"type" : "id",
"value" : "1234"
}
]
}
SPEC (that will run but is not correct)
[
{
"operation": "shift",
"spec": {
// Loop all listitems
"listitems": {
"*": {
// Get the value of the current item and push to processlist.type array
"#": "processlist[#2].type",
// Here is the problem, I need to get the top level value for the current value/key
// Forcing this to "name" will at least execute the code
"#(2,name)": "processlist[#2].value"
}
}
}
}
]
Any ideas?
You can proceed one more step by adding "*" key to nest the current spec more while roaming by #(3,&) dynamically as this ampersand represents the incurred key values name and id such as
[
{
"operation": "shift",
"spec": {
"listitems": {
"*": {
"*": {
"#1": "processlist[#3].type",
"#(3,&)": "processlist[#3].value"
}
}
}
}
}
]
I have below json & I need to replace employee_names to emp_id from a map.
Tried this GolangPlay , but not sure how to replace values from the given map and error handling if the value is not present in given map.
Json data:
[
{
"dept": "IT",
"condition": {
"employee": [
"emp1"
]
}
},
{
"dept": "HR",
"condition": {
"employee": [
"emp2",
"emp3"
]
}
}
]
Map data:
[{emp1 14325} {emp3 49184} {emp2 21518}]
Expected output:
[
{
"dept": "IT",
"condition": {
"employee": [
14325
]
}
},
{
"dept": "HR",
"condition": {
"employee": [
21518,
49184
]
}
}
]
code :
Started with below code , but not sure how to use the given map to replace with error handling.
package main
import (
"encoding/json"
"fmt"
//"strconv"
//"log"
)
func main() {
jsonStr := `[
{
"dept": "IT",
"condition": {
"employee": [
"emp1"
]
}
},
{
"dept": "HR",
"condition": {
"employee": [
"emp2",
"emp3"
]
}
}
]`
empMap := `[{emp1 14325} {emp3 49184} {emp2 21518}]`
type GetEmployee []struct {
Dept string `json:"dept"`
Condition struct {
Employee []string `json:"employee"`
} `json:"condition"`
}
var empResponse GetEmployee
unmarshallingError := json.Unmarshal([]byte(string(jsonStr)), &empResponse)
if unmarshallingError != nil {
fmt.Println(unmarshallingError.Error())
}
fmt.Println(empResponse)
fmt.Println(empMap)
for i := range empResponse {
fmt.Println(i)
}
}
Instead of storing the ids in an array of {ids: value}, it will be better for them to be in a map instead.
The above will range over the Employees' name and change it to the id. A check is made to see if there is a certain id key in the map.
for i, e := range empResponse {
fmt.Println(e)
for j,val := range empResponse[i].Condition.Employee {
if _, ok := ids[val]; ok {
empResponse[i].Condition.Employee[j] = ids[val]
}
}
}
Full code
package main
import (
"encoding/json"
"fmt"
//"strconv"
//"log"
)
func main() {
jsonStr := `[
{
"dept": "IT",
"condition": {
"employee": [
"emp1"
]
}
},
{
"dept": "HR",
"condition": {
"employee": [
"emp2",
"emp3"
]
}
}
]`
empMap := `{"emp1": "14325", "emp3": "49184", "emp2": "21518"}`
type GetEmployee []struct {
Dept string `json:"dept"`
Condition struct {
Employee []string `json:"employee"`
} `json:"condition"`
}
var empResponse GetEmployee
var ids map[string]string
unmarshallingError := json.Unmarshal([]byte(string(jsonStr)), &empResponse)
if unmarshallingError != nil {
fmt.Println(unmarshallingError.Error())
}
json.Unmarshal([]byte(empMap), &ids)
fmt.Println(empResponse)
fmt.Println(ids)
for i, e := range empResponse {
fmt.Println(e)
for j,val := range empResponse[i].Condition.Employee {
if _, ok := ids[val]; ok {
empResponse[i].Condition.Employee[j] = ids[val]
}
}
}
fmt.Println(empResponse)
}
playground
In the above, the id are strings since the name to be replaced are strings. Actually Employee are of type []string. If a string is to be replaced by an int, then Employee type needs to be changed to []interface{}.
playground
Problem is, I want to sort by price an array of users that might contains empty attributes like this one:
var array = [
{
"created": "2015-11-27T16:33:46.781Z",
"name": "Johan",
"pricing": {
"base_price" : "12",
"price_by_hour" : "5"
}
},
{
"created": "2015-11-27T16:33:46.781Z",
"name": "Marco"
},
{
"created": "2015-11-27T16:33:46.781Z",
"name": "Jane",
"pricing": {
"base_price" : "8",
"price_by_hour" : "11"
}
}
];
array = _.sortBy(array, function(item) {
return item.pricing.base_price;
});
console.log(array);
TypeError: Cannot read property 'base_price' of undefined
How can I put the items without the pricing object at the bottom of my list and still sorting it?
In this case, I want to sort the list with Jane first, then Johan, then Marco.
Here's an easy way to do it:
_.sortBy(array, 'pricing.base_price');
When you pass a string as an iteratee to sortBy(), the property() function is used. This function works with property paths and simply returns undefined if the property doesn't exist.
Just put a conditional
array = _.sortBy(array, function(item){
if(item.pricing){
return item.pricing.base_price;
}
});
OK, I just had to return false if the attribute is empty:
array = _.sortBy(array, function(item) {
if(!item.pricing || !item.pricing.base_price){
return -1;
}
return item.pricing.base_price;
});
You need a conditional to avoid getting TypeError. Also you need to cast base_price to Number to get a proper sorting.
array = _.sortBy(array, function(item){
if(item.pricing){
return Number(item.pricing.base_price);
}
});
One alternative would be already initate them as Number.
var array = [
{
"created": "2015-11-27T16:33:46.781Z",
"name": "Johan",
"pricing": {
"base_price" : 12,
"price_by_hour" : 5
}
},
{
"created": "2015-11-27T16:33:46.781Z",
"name": "Marco"
},
{
"created": "2015-11-27T16:33:46.781Z",
"name": "Jane",
"pricing": {
"base_price" : 8,
"price_by_hour" : 11
}
}
];
array = _.sortBy(array, function(item) {
if(item.pricing){
return item.pricing.base_price;
}
});