How to map and reduce List of Maps in Elixir - functional-programming

What's a good way to map and reduce the elixir's list and convert it into new list.
Requirement:
1. Find the maps with same id:
2. Merge the values of "role" key (i.e. Gather all the unique values).
3. For all other maps(elements of list), do nothing.
list = [%{"id": 1, "role": ["A", "B"]}, %{"id": 2, "role": ["B", "C"]}, %{"id": 1, "role": ["C", "A"]} ]
needs to be converted in the following list:
ans_list = [%{"id": 1, "role": ["A", "B", "C"]}, %{"id": 2, "role": ["B", "C"]}]

You can use Enum.group_by/2 to group by the id, then for each group, pass the role to Enum.flat_map/2 and Enum.uniq/1:
list = [%{"id": 1, "role": ["A", "B"]}, %{"id": 2, "role": ["B", "C"]}, %{"id": 1, "role": ["C", "A"]} ]
list
|> Enum.group_by(&(&1.id))
|> Enum.map(fn {key, value} ->
%{id: key, role: value |> Enum.flat_map(&(&1.role)) |> Enum.uniq}
end)
|> IO.inspect
Output:
[%{id: 1, role: ["A", "B", "C"]}, %{id: 2, role: ["B", "C"]}]
As requested in a comment below, here's how to preserve all key/value pairs and only modify role of the first item of a group:
list = [%{"id": 1, "role": ["A", "B"], "somekey": "value of the key 1"},
%{"id": 2, "role": ["B", "C"], "somekey": "value of the key 2"},
%{"id": 1, "role": ["C", "A"], "somekey": "value of the key 3"}]
list
|> Enum.group_by(&(&1.id))
|> Enum.map(fn {_, [value | _] = values} ->
%{value | role: values |> Enum.flat_map(&(&1.role)) |> Enum.uniq}
end)
|> IO.inspect
Output:
[%{id: 1, role: ["A", "B", "C"], somekey: "value of the key 1"},
%{id: 2, role: ["B", "C"], somekey: "value of the key 2"}]

Related

Delete string from names in sublists

I have a list of lists I need to delete "gene-" everywhere where it happens.
I tried
lapply(net, FUN = function(x) setNames(x, sub("gene-","", x)))
but I get the error
Error in names(object) <- nm : attempt to set an attribute on NULL
head(net)
$colors
gene-AAAS gene-AAK1 gene-AAMDC gene-AAMP gene-AARS1 gene-AASDH
"magenta" "brown" "purple" "darkgrey" "brown" "blue"
gene-AASDHPPT gene-AASS gene-AATK gene-ABAT
[ reached getOption("max.print") -- omitted 8990 entries ]
$unmergedColors
gene-AAAS gene-AAK1 gene-AAMDC gene-AAMP gene-AARS1 gene-AASDH
"darkgrey" "blue" "magenta" "darkolivegreen" "blue" "brown"
gene-AASDHPPT gene-AASS gene-AATK gene-ABAT gene-ABCA1 gene-ABCA12
"lightyellow" "lightgreen" "turquoise" "darkred" "turquoise" "grey60"
[ reached getOption("max.print") -- omitted 8990 entries ]
$MEs
MEblack MEgreenyellow MElightcyan MEyellow MEturquoise MEpink MEwhite MEdarkred
M5 -0.17423916 0.141440817 0.23401244 0.36358728 -0.0220835 -0.18126013 0.05942248 -0.45035371
N3 0.47690393 0.428961135 0.07241255 -0.02557197 0.2238352 0.06742087 -0.09574663 0.52201599
$goodSamples
[1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
$goodGenes
[1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[ reached getOption("max.print") -- omitted 8990 entries ]
$dendrograms
$dendrograms[[1]]
Call:
fastcluster::hclust(d = as.dist(dissTom), method = "average")
Cluster method : average
Number of objects: 9990
dput(net)
166L, 5768L, 2346L, 7132L, 625L, 4848L, 736L, 7001L,
1721L, 6626L, 7674L, 2543L, 7013L, 8667L, 4593L, 2804L,
....
7435L, 4895L, 8462L, 1732L, 3160L, 8529L), labels = NULL,
method = "average", call = fastcluster::hclust(d = as.dist(dissTom),
method = "average"), dist.method = NULL), class = "hclust")),
TOMFiles = NULL, blockGenes = list(1:9990), blocks = c(1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
...
1, 1), MEsOK = TRUE)
Your code almost works, you need two changes:
You want to modify the names, so your inner function needs to read names(x):
function(x) setNames(x, sub("gene-", "", names(x)))
net contains a lot of members. You only want to replace the colors and unmergedColors members, so apply your function to only those. Oh, and you need to assign the result back to your object:
which = c("colors", "unmergedColors")
net[which] = lapply(net[which], function(x) setNames(x, sub("gene-", "", names(x))))

R Plumber, How to create nested JSON

Need Help, I just create API with R Plumber, I just would like the Result Json will be like this
Instead of like this
below are my R Plumber script
library(plumber)
library(jsonlite)
#* #get /nested_json
#* #serializer unboxedJSON
function() {
main_info <- data.frame(
Name = "John",
Address = "Stret No. 1",
Point = "600"
)
reason_info <- data.frame(code = c('AAA', 'BBB', 'CCC', NA, 'EEE'),
descriptiom = c('low value of point A', 'low value of point B', 'low value of point C', NA, 'low value of point D') )
main_info[1, "reason"][[1]] <- list(reason_info)
final_output <- as.list(main_info)
final_output
}
Thanks a lot for any kind of suggestion
UPDATE
The alternative solution from #deschen just solve the problem, my teammates that created the Web Application could use this solution.
the alternative solution are like below :
{
"Name": "John",
"Address": "Stret No. 1",
"Point": "600",
"reason": [
{
"code": "AAA",
"description": "low value of point A"
},
{
"code": "BBB",
"description": "low value of point B"
},
{
"code": "CCC",
"description": "low value of point C"
},
{
"code": null,
"description": null
},
{
"code": "EEE",
"description": "low value of point D"
}
]
}
library(tidyverse)
library(jsonlite)
reason_info <- data.frame(code = c('AAA', 'BBB', 'CCC', NA, 'EEE'),
description = c('low value of point A',
'low value of point B',
'low value of point C',
NA,
'low value of point D'))
reason_info_new <- reason_info %>%
mutate(new = apply(across(everything()), 1, as.list)) %>%
pull(new)
full <- list(Name = "John",
Address = "Stret No. 1",
Point = "600",
reason = reason_info_new)
myJSON <- toJSON(full, pretty = TRUE, auto_unbox = TRUE)
myJSON
which gives:
{
"Name": "John",
"Address": "Stret No. 1",
"Point": "600",
"reason": [
{
"code": "AAA",
"description": "low value of point A"
},
{
"code": "BBB",
"description": "low value of point B"
},
{
"code": "CCC",
"description": "low value of point C"
},
{
"code": null,
"description": null
},
{
"code": "EEE",
"description": "low value of point D"
}
]
}

Use your own csv file instead of json

I would like to run locally this repository
However I have my own csv file with data which I would like to use instead of the json example.
How is it possible to make it?
Here and example of data
data = data.frame(id = c(1,1,2,2), col = c("before", "after", "before", "after"), text = c("text 3 sentiment","I feel good","Happy","It is a nice day and I feel glad"))
I'm not sure... but based off your data...
> read.csv2("file.csv") %>% jsonlite::toJSON(auto_unbox = TRUE, pretty = TRUE)
[
{
"id": 1,
"col": "before",
"text": "text 3 sentiment"
},
{
"id": 1,
"col": "after",
"text": "I feel good"
},
{
"id": 2,
"col": "before",
"text": "Happy"
},
{
"id": 2,
"col": "after",
"text": "It is a nice day and I feel glad"
}
]

Once a dropdown option is selected, how do I "change.emit" or "trigger change" on the plot?

Any ideas what's supposed to go where the triple '?'s are?
import pandas as pd
from bokeh.layouts import column
from bokeh.models import CustomJS, ColumnDataSource, Slider, Select
import bokeh.plotting as bp
from bokeh.plotting import Figure, output_file, show
from bokeh.models import HoverTool, DatetimeTickFormatter
# Create an output file
bp.output_file('columnDataSource.html')
# Create your plot as a bokeh.figure object
myPlot = bp.figure(height = 600,
width = 800,
y_range=(0,3))
x_values = [1, 2, 3, 4, 5]
y_values = [1, 2, 3, 4, 5]
myPlot.line(x = x_values, y= y_values, line_width=2)
callback = CustomJS(args={
'source1': {'x': [1,2,3,4], 'y':[1,1,1,1]},
'source2': {'x': [0,0,0,0], 'y':[2,2,2,2]},
'source3': {'x': [1,2,3,4], 'y':[1,1,1,1]}},
code="""
var data1 = source1;
var data2 = source2;
var data3 = source3;
var f = cb_obj.value;
if(f == 'A'){
console.log("A selected from dropdown.");
data1.x = data1.x;
data1.y = data1.y;
}
else if(f == 'B'){
// Substitute all old data1 values in with data2 values
console.log("B selected from dropdown.");
data1.x = data2.x;
data1.y = data2.y;
}
else{
console.log("C selected.");
// Substitute all old data1 values in with data3 values
data1.x = data3.x;
data1.y = data3.y;
}
// Problematic line!
???.change.emit();
""")
select = Select(title='Choose', value='A', options=['A','B','C'])
select.js_on_change('value', callback)
layout = column(select, myPlot)
show(layout) # et voilĂ .
I expect my x and y values to change and plot accordingly to my Bokeh graph.
Nothing is changing at the moment as I don't know what object's "trigger" function I'm supposed to be calling. Please help, I'm new to Bokeh.
You do ColumnDataSource.change.emit() if you updated the data source fields by reference e.g. when you update only x or only y:
ColumnDataSource.data['x'] = [4, 3, 2, 1]
ColumnDataSource.change.emit()
When you update them both you do:
ColumnDataSource.data = new_data
Where new_data is a new json object like {'x': [1], 'y':[2]}.
The reason for this is that JS can automatically detect a change when existing object is replaced with a new one but it cannot detect changes by reference so in those cases you need explicitly to call: ColumnDataSource.change.emit() to update the BokehJS model.
Here is your modified code:
from bokeh.models import CustomJS, ColumnDataSource, Select, Column
from bokeh.plotting import figure, show
myPlot = figure(y_range = (0, 4))
data = {'A': {'x': [1, 2, 3, 4], 'y':[1, 1, 1, 1]},
'B': {'x': [1, 2, 3, 4], 'y':[2, 2, 2, 2]},
'C': {'x': [1, 2, 3, 4], 'y':[3, 3, 3, 3]} }
source = ColumnDataSource(data['A'])
myPlot.line('x', 'y', line_width = 2, source = source)
callback = CustomJS(args = {'source': source, 'data': data},
code = """source.data = data[cb_obj.value]; """)
select = Select(title = 'Choose', value = 'A', options = ['A', 'B', 'C'])
select.js_on_change('value', callback)
layout = Column(select, myPlot)
show(layout)

Julia Plotly Plots

I'm trying to do some really basic scatterplots. I'm following instructions from here: https://plot.ly/julia/subplots/
using Plotly
trace1 = [
"x" => [1, 2, 3],
"y" => [4, 5, 6],
"type" => "scatter"
]
trace2 = [
"x" => [20, 30, 40],
"y" => [50, 60, 70],
"xaxis" => "x2",
"yaxis" => "y2",
"type" => "scatter"
]
data = [trace1, trace2]
layout = [
"xaxis" => ["domain" => [0, 0.45]],
"yaxis2" => ["anchor" => "x2"],
"xaxis2" => ["domain" => [0.55, 1]]
]
response = Plotly.plot(data, ["layout" => layout, "filename" => "simple-subplot", "fileopt" => "overwrite"])
plot_url = response["url"]
I get:
ERROR: MethodError: no method matching PlotlyJS.Plot(::Array{Array{Pair{String,Any},1},1}, ::Array{Pair{String,Any},1})
Closest candidates are:
PlotlyJS.Plot(::AbstractArray{T<:Union{AbstractString, Date, Number, Symbol},1}, ::AbstractArray{T,1} where T) where T<:Union{AbstractString, Date, Number, Symbol} at /Users/username/.julia/v0.6/PlotlyJS/src/convenience_api.jl:56
PlotlyJS.Plot(::AbstractArray{T<:Union{AbstractString, Date, Number, Symbol},1}, ::AbstractArray{T,1} where T, ::PlotlyJS.Layout; kind, style, kwargs...) where T<:Union{AbstractString, Date, Number, Symbol} at /Users/username/.julia/v0.6/PlotlyJS/src/convenience_api.jl:56
PlotlyJS.Plot(::AbstractArray{T<:(AbstractArray{T,1} where T),1}, ::AbstractArray{T,2} where T) where T<:(AbstractArray{T,1} where T) at /Users/username/.julia/v0.6/PlotlyJS/src/convenience_api.jl:68
...
Stacktrace:
[1] #plot#36(::Array{Any,1}, ::Function, ::Array{Array{Pair{String,Any},1},1}, ::Vararg{Any,N} where N) at /Users/username/.julia/v0.6/PlotlyJS/src/display.jl:76
[2] plot(::Array{Array{Pair{String,Any},1},1}, ::Vararg{Any,N} where N) at /Users/username/.julia/v0.6/PlotlyJS/src/display.jl:76
I see the errors invoke PlotlyJS vs regular Plotly. I copy/pasted the example code into a fresh Julia shell. I've done Pkg.add("PlotlyJS"), Pkg.add("Plotly"), and Pkg.update(). Still no luck...
My reading of the documentation suggests that this is the kind of approach to try:
using PlotlyJS
t1 = scatter(;x=[1, 2, 3, 4, 5],
y=[1, 6, 3, 6, 1],
mode="lines",
name="Team A",
text=["A-1", "A-2", "A-3", "A-4", "A-5"])
t2 = scatter(;x=[1, 2, 3, 4, 5],
y=[10, 60, 30, 60, 10],
mode="lines",
name="Team B",
text=["B-1", "B-2", "B-3", "B-4", "B-5"])
layout = Layout(;title="Data Labels Hover",
xaxis_range=[0, 10],
yaxis_range=[0, 100])
data = [t1, t2]
PlotlyJS.plot(data, layout)
I think I'd recommend using Plots.jl for local plotting, though (which can use PlotlyJS.jl). Presumably Plotly.jl is for plotting to the cloud?

Resources