how to pivot a table represented as scala List of Maps by taking one column and converting it to row? - scala

I have following table data represented as scala's List[Map[String, Any]]
Following is scala code for creating such list of map
val inputData = List(
Map(
"d1_name" -> "t1",
"d1_id" -> 1,
"d2_name" -> "p1",
"d2_id" -> 11,
"value1" -> 5,
"value2" -> 12,
"value3" -> 30
),
Map(
"d1_name" -> "t1",
"d1_id" -> 1,
"d2_name" -> "p2",
"d2_id" -> 22,
"value1" -> 10,
"value2" -> 14,
"value3" -> 300
),
Map(
"d1_name" -> "t2",
"d1_id" -> 2,
"d2_name" -> "p3",
"d2_id" -> 33,
"value1" -> 15,
"value2" -> 16,
"value3" -> 33
),
Map(
"d1_name" -> "t3",
"d1_id" -> 3,
"d2_name" -> "p7",
"d2_id" -> 7,
"value1" -> 5,
"value2" -> 8,
"value3" -> 17
)
)
I want to convert shown input table to following pivoted output table in scala, the operation is to pivot "d1_name" column over value3 data, also dropping "d1_id" column in final output.
(i am expecting same List[Map[String, Any]] output type)
following is pictorial representation of output.

Try like this:
Use GroupBy with the column d1_name and for each element in the grouped list get modify data as required.
inputData.groupBy(_.getOrElse("d1_name", "")).flatMap(e => {
e._2.map(list => {
Map(
"d2_name" -> list.getOrElse("d2_name", ""),
"d2_id" -> list.getOrElse("d2_id", ""),
"value1" -> list.getOrElse("value1", 0),
"value2" -> list.getOrElse("value2", 0),
"value3" -> list.getOrElse("value3", 0),
if (e._1 == "t1") {"t1-value3" -> list.getOrElse("value3", 0)} else {"t1-value3" -> 0},
if (e._1 == "t2") {"t2-value3" -> list.getOrElse("value3", 0)} else {"t2-value3" -> 0},
if (e._1 == "t3") {"t3-value3" -> list.getOrElse("value3", 0)} else {"t3-value3" -> 0}
)
}
)
})
// List(
Map(value3 -> 30, t3-value3 -> 0, t2-value3 -> 0, d2_name -> p1, value1 -> 5, t1-value3 -> 30, value2 -> 12, d2_id -> 11),
Map(value3 -> 300, t3-value3 -> 0, t2-value3 -> 0, d2_name -> p2, value1 -> 10, t1-value3 -> 300, value2 -> 14, d2_id -> 22),
Map(value3 -> 17, t3-value3 -> 17, t2-value3 -> 0, d2_name -> p7, value1 -> 5, t1-value3 -> 0, value2 -> 8, d2_id -> 7),
Map(value3 -> 33, t3-value3 -> 0, t2-value3 -> 33, d2_name -> p3, value1 -> 15, t1-value3 -> 0, value2 -> 16, d2_id -> 33)
)
Hope this helps you.
---EDIT----
To generate the keys dynamically
val groupedInput = inputData.groupBy(_.getOrElse("d1_name", ""))
val uniqueKeys = groupedInput.keySet
groupedInput.flatMap(e => {
e._2.map(list => {
val value3= uniqueKeys.map(key => if (e._1 == key) {key+"-value3" -> list.getOrElse("value3", 0)} else {key+"-value3" -> 0})
Map(
List("d2_name" -> list.getOrElse("d2_name", ""),
"d2_id" -> list.getOrElse("d2_id", ""),
"value1" -> list.getOrElse("value1", 0),
"value2" -> list.getOrElse("value2", 0),
"value3" -> list.getOrElse("value3", 0)).++(value3) :_*
)
}
)
})

This version pivots "value3" based on the value of "d1_name", and deletes "d1_id". It leaves other values untouched. In a proper implementation these would be taken out as parameters, and there would be more error checking.
val defaults = // Compute default "table" for new pivot values
inputData
.map(_("d1_name").toString + "-" + "value3" -> 0)
.toMap
val pivot = // Add new pivot values to existing "table"
inputData.map { map =>
val d1Name = map("d1_name") + "-" + "value3"
map ++ defaults + (d1Name -> map("value3")) - "d1_name" - "d1_id"
}

Related

GTK application error occurred on aarch64-based target board

I coded the following simple GTK application. The target board that I use is TurboX-C610, that is manufactured by Thundercomm, and the firmware version is 200007.1.
#include <gtk/gtk.h>
int main(int argc, char *argv[]) {
GtkWidget *window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_show(window);
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_main();
return 0;
}
I compiled the code by using cross compiler of Turbo-C610 that is provided by manufacturer. After doing that, I executed the program on target board by using ADB. But the following error is occurred. Because window system is Wayland, I do export GDK_DEGUG=wayland.
(mytest:5695): Gdk-WARNING **: 07:52:13.039: Error 22 (Invalid argument) dispatching to Wayland display.
When I did export WAYLAND_DEBUG=1, the logs are following,
[ 89809.336] -> wl_display#1.get_registry(new id wl_registry#2)
[ 89809.495] -> wl_display#1.sync(new id wl_callback#3)
[ 89809.832] wl_display#1.delete_id(3)
[ 89809.904] wl_registry#2.global(1, "wl_compositor", 3)
[ 89810.066] -> wl_registry#2.bind(1, "wl_compositor", 3, new id [unknown]#4)
[ 89810.190] wl_registry#2.global(2, "wl_subcompositor", 1)
[ 89810.278] -> wl_registry#2.bind(2, "wl_subcompositor", 1, new id [unknown]#5)
[ 89810.400] wl_registry#2.global(3, "wl_scaler", 2)
[ 89810.477] wl_registry#2.global(4, "presentation", 1)
[ 89810.560] wl_registry#2.global(5, "wl_pll", 1)
[ 89810.628] wl_registry#2.global(6, "wl_data_device_manager", 2)
[ 89810.713] -> wl_registry#2.bind(6, "wl_data_device_manager", 1, new id [unknown]#6)
[ 89810.827] wl_registry#2.global(7, "wl_shm", 1)
[ 89810.907] -> wl_registry#2.bind(7, "wl_shm", 1, new id [unknown]#7)
[ 89811.022] wl_registry#2.global(8, "wayland_buffer_backend", 4)
[ 89811.101] wl_registry#2.global(9, "wl_output", 3)
[ 89811.178] -> wl_registry#2.bind(9, "wl_output", 2, new id [unknown]#8)
[ 89811.303] -> wl_display#1.sync(new id wl_callback#9)
[ 89811.353] wl_registry#2.global(10, "zlinux_dmabuf", 1)
[ 89811.437] wl_registry#2.global(11, "gbm_buffer_backend", 1)
[ 89811.521] wl_registry#2.global(12, "wl_input_panel", 1)
[ 89811.605] wl_registry#2.global(13, "wl_text_input_manager", 1)
[ 89811.684] wl_registry#2.global(14, "wl_shell", 1)
[ 89811.768] wl_registry#2.global(15, "xdg_shell", 1)
[ 89811.843] -> wl_registry#2.bind(15, "xdg_shell", 1, new id [unknown]#10)
[ 89811.927] -> xdg_shell#10.use_unstable_version(5)
[ 89811.965] wl_registry#2.global(16, "desktop_shell", 3)
[ 89812.003] wl_registry#2.global(17, "workspace_manager", 1)
[ 89812.062] wl_registry#2.global(18, "screenshooter", 1)
[ 89812.147] wl_callback#3.done(0)
[ 89812.337] wl_display#1.delete_id(9)
[ 89812.362] wl_output#8.geometry(0, 0, 62, 110, 1, "unknown", "unknown", 0)
[ 89812.413] wl_output#8.scale(1)
[ 89812.422] wl_output#8.done()
[ 89812.432] wl_callback#9.done(0)
[ 89849.534] -> wl_shm#7.create_pool(new id wl_shm_pool#9, fd 6, 4096)
[ 89849.627] -> wl_shm_pool#9.resize(8832)
[ 89849.679] -> wl_shm_pool#9.resize(18624)
[ 89851.662] -> wl_compositor#4.create_surface(new id wl_surface#3)
[ 89851.704] -> wl_surface#3.set_opaque_region(nil)
[ 89851.720] -> wl_surface#3.set_input_region(nil)
[ 89852.011] -> wl_compositor#4.create_region(new id wl_region#11)
[ 89852.033] -> wl_surface#3.set_opaque_region(wl_region#11)
[ 89852.045] -> wl_region#11.destroy()
[ 89852.107] -> wl_compositor#4.create_region(new id wl_region#12)
[ 89852.120] -> wl_region#12.add(16, 13, 20, 20)
[ 89852.151] -> wl_surface#3.set_input_region(wl_region#12)
[ 89852.161] -> wl_region#12.destroy()
[ 89852.176] -> wl_compositor#4.create_region(new id wl_region#13)
[ 89852.185] -> wl_region#13.add(16, 13, 20, 20)
[ 89852.204] -> wl_surface#3.set_input_region(wl_region#13)
[ 89852.215] -> wl_region#13.destroy()
[ 89856.835] -> wl_surface#3.set_input_region(nil)
[ 89856.993] -> wl_surface#3.set_input_region(nil)
[ 89857.016] -> wl_surface#3.set_input_region(nil)
[ 89857.055] -> wl_surface#3.set_input_region(nil)
[ 89857.142] -> wl_surface#3.set_input_region(nil)
[ 89857.238] -> wl_surface#3.set_input_region(nil)
[ 89857.322] -> xdg_shell#10.get_xdg_surface(new id xdg_surface#14, wl_surface#3)
[ 89857.338] -> xdg_surface#14.set_parent(nil)
[ 89857.349] -> xdg_surface#14.set_title("mytest")
[ 89857.357] -> xdg_surface#14.set_window_geometry(26, 23, 59, 52)
[ 89857.380] -> xdg_surface#14.set_app_id("mytest")
[ 89868.738] -> wl_compositor#4.create_region(new id wl_region#15)
[ 89868.781] -> wl_region#15.add(33, 23, 45, 7)
[ 89868.795] -> wl_region#15.add(26, 30, 59, 45)
[ 89868.803] -> wl_surface#3.set_opaque_region(wl_region#15)
[ 89868.807] -> wl_region#15.destroy()
[ 89868.904] -> wl_compositor#4.create_region(new id wl_region#16)
[ 89868.920] -> wl_region#16.add(16, 13, 79, 72)
[ 89868.970] -> wl_surface#3.set_input_region(wl_region#16)
[ 89868.980] -> wl_region#16.destroy()
[ 89870.653] -> wl_compositor#4.create_region(new id wl_region#17)
[ 89870.684] -> wl_region#17.add(33, 23, 79, 7)
[ 89870.701] -> wl_region#17.add(26, 30, 93, 45)
[ 89870.719] -> wl_surface#3.set_opaque_region(wl_region#17)
[ 89870.728] -> wl_region#17.destroy()
[ 89870.777] -> wl_compositor#4.create_region(new id wl_region#18)
[ 89870.788] -> wl_region#18.add(16, 13, 113, 72)
[ 89870.808] -> wl_surface#3.set_input_region(wl_region#18)
[ 89870.818] -> wl_region#18.destroy()
[ 89870.951] -> wl_shm#7.create_pool(new id wl_shm_pool#19, fd 7, 60320)
[ 89870.980] -> wl_shm_pool#19.create_buffer(new id wl_buffer#20, 0, 145, 104, 580, 0)
[ 89872.995] -> wl_surface#3.attach(wl_buffer#20, 0, 0)
[ 89873.037] -> wl_surface#3.set_buffer_scale(1)
[ 89873.048] -> wl_surface#3.damage(0, 0, 145, 104)
[ 89873.083] -> wl_surface#3.frame(new id wl_callback#21)
[ 89873.095] -> wl_surface#3.commit()
[08:49:23.495] libwayland: file descriptor expected, object (7), message create_pool(nhi)
[ 89873.497] wl_display#1.delete_id(11)
[ 89873.524] wl_display#1.delete_id(12)
[ 89873.527] wl_display#1.delete_id(13)
[ 89873.543] wl_display#1.delete_id(15)
[ 89873.553] wl_display#1.delete_id(16)
[ 89873.563] wl_display#1.delete_id(17)
[ 89873.573] wl_display#1.delete_id(18)
[ 89873.583] wl_display#1.error(wl_display#1, 1, "invalid arguments for wl_shm#7.create_pool")
The error is related to wl_display_create_pool. So I researched on wl_shm and pool and relation of Wayland and GTK, but I can't get any solutions.
To solve this problem, I wonder what I should do.

How to get an array of property values from an array of structs [duplicate]

I want to create a generic function to sort an array of classes based on a property passed.
For example, I have these classes
public class Car {
var id: Int
var manufacturer: String
var variant: String
init(id: Int, manufacturer: String, variant: String) {
self.id = id
self.manufacturer = manufacturer
self.variant = variant
}
}
enum Gender {
case male
case female
}
public class Person {
var id: Int
var name: String
var age: Int
var gender: Gender
init(id: Int, name: String, age: Int, gender: Gender) {
self.id = id
self.name = name
self.age = age
self.gender = gender
}
}
And these arrays,
let cars = [
Car(id: 1, manufacturer: "Ford", variant: "Focus"),
Car(id: 2, manufacturer: "Nissan", variant: "Skyline"),
Car(id: 3, manufacturer: "Dodge", variant: "Charger"),
Car(id: 4, manufacturer: "Chevrolet", variant: "Camaro"),
Car(id: 5, manufacturer: "Ford", variant: "Shelby")
]
let persons = [
Person(id: 1, name: "Ed Sheeran", age: 26, gender: .male),
Person(id: 2, name: "Phil Collins", age: 66, gender: .male),
Person(id: 3, name: "Shakira", age: 40, gender: .female),
Person(id: 4, name: "Rihanna", age: 25, gender: .female),
Person(id: 5, name: "Bono", age: 57, gender: .male)
]
How to write a generic extension for the array, to sort it based on the property passed? (eg. persons.sort(name) or cars.sort(manufacturer))
Thanks!
Here you go:
extension Array {
mutating func propertySort<T: Comparable>(_ property: (Element) -> T) {
sort(by: { property($0) < property($1) })
}
}
Usage:
persons.propertySort({$0.name})
And here is a non-mutating version:
func propertySorted<T: Comparable>(_ property: (Element) -> T) -> [Element] {
return sorted(by: {property($0) < property($1)})
}
As Leo Dabus pointed out, you can generalise the extension to any MutableCollection that is also a RandomAccessCollection:
extension MutableCollection where Self : RandomAccessCollection {
...
Starting with Swift 4 you can define a sorting method which takes
a Key-Path Expression as argument. As Leo points out, these methods can be defined more generally as protocols extension methods (for mutable collections and sequences, respectively):
extension MutableCollection where Self: RandomAccessCollection {
// Mutating in-place sort:
mutating func sort<T: Comparable>(byKeyPath keyPath: KeyPath<Element, T>) {
sort(by: { $0[keyPath: keyPath] < $1[keyPath: keyPath] })
}
}
extension Sequence {
// Non-mutating sort, returning a new array:
func sorted<T: Comparable>(byKeyPath keyPath: KeyPath<Element, T>) -> [Element] {
return sorted(by: { $0[keyPath: keyPath] < $1[keyPath: keyPath] })
}
}
Example usage:
persons.sort(byKeyPath: \.name)
cars.sort(byKeyPath: \.manufacturer)
For more information about key-path expressions, see SE-0161 Smart KeyPaths: Better Key-Value Coding for Swift.
edit/update:
For Xcode 13.0+, iOS 15.0+, iPadOS 15.0+, macOS 12.0+, Mac Catalyst 15.0+, tvOS 15.0+, watchOS 8.0+ you can use KeyPathComparator:
let sortedPeople1 = people.sorted(using: KeyPathComparator(\.age)) // [{id 4, name "Rihanna", age 25, female}, {id 1, name "Ed Sheeran", age 26, male}, {id 3, name "Shakira", age 40, female}, {id 5, name "Bono", age 57, male}, {id 2, name "Phil Collins", age 66, male}]
let sortedPeople2 = people.sorted(using: KeyPathComparator(\.age, order: .reverse)) // [{id 2, name "Phil Collins", age 66, male}, {id 5, name "Bono", age 57, male}, {id 3, name "Shakira", age 40, female}, {id 1, name "Ed Sheeran", age 26, male}, {id 4, name "Rihanna", age 25, female}]
You can also use multiple sorting criteria and order:
let sortedPeople3 = people.sorted(using: [KeyPathComparator(\.age, order: .reverse), KeyPathComparator(\.name)]) // [{id 2, name "Phil Collins", age 66, male}, {id 5, name "Bono", age 57, male}, {id 3, name "Shakira", age 40, female}, {id 1, name "Ed Sheeran", age 26, male}, {id 4, name "Rihanna", age 25, female}]
let sortedPeople4 = people.sorted(using: [KeyPathComparator(\.age, order: .reverse), KeyPathComparator(\.name)]) // [{id 2, name "Phil Collins", age 66, male}, {id 5, name "Bono", age 57, male}, {id 3, name "Shakira", age 40, female}, {id 1, name "Ed Sheeran", age 26, male}, {id 4, name "Rihanna", age 25, female}]
original answer
Expanding on #MartinR answer and #Sweeper answer to allow increasing (<) or decreasing (>) sort as well as throw and default sort ascending methods:
extension MutableCollection where Self: RandomAccessCollection {
mutating func sort<T: Comparable>(_ predicate: (Element) throws -> T) rethrows {
try sort(predicate, by: <)
}
mutating func sort<T: Comparable>(_ predicate: (Element) throws -> T, by areInIncreasingOrder: ((T, T) throws -> Bool)) rethrows {
try sort { try areInIncreasingOrder(predicate($0), predicate($1)) }
}
}
extension Sequence {
func sorted<T: Comparable>(_ predicate: (Element) throws -> T) rethrows -> [Element] {
try sorted(predicate, by: <)
}
func sorted<T: Comparable>(_ predicate: (Element) throws -> T, by areInIncreasingOrder: ((T,T) throws -> Bool)) rethrows -> [Element] {
try sorted { try areInIncreasingOrder(predicate($0), predicate($1)) }
}
}
people.sorted(\.age)
people.sorted(\.age, by: >)
cars.sorted(\.manufacturer)
cars.sorted(\.manufacturer, by: >)
edit/update:
To suport sorting a custom object by an optional property that conforms to Comparable protocol:
extension MutableCollection where Self: RandomAccessCollection {
mutating func sort<T: Comparable>(_ predicate: (Element) throws -> T?) rethrows {
try sort(predicate, by: <)
}
mutating func sort<T: Comparable>(_ predicate: (Element) throws -> T?, by areInIncreasingOrder: ((T, T) throws -> Bool)) rethrows {
try sort(by: {
switch try (predicate($0), predicate($1)) {
case let (lhs?, rhs?): return try areInIncreasingOrder(lhs, rhs)
case (.none, _): return false
case (_, .none): return true
}
})
}
}
extension Sequence {
func sorted<T: Comparable>(_ predicate: (Element) throws -> T?) rethrows -> [Element] {
try sorted(predicate, by: <)
}
func sorted<T: Comparable>(_ predicate: (Element) throws -> T?, by areInIncreasingOrder: ((T,T) throws -> Bool)) rethrows -> [Element] {
try sorted(by: {
switch try (predicate($0), predicate($1)) {
case let (lhs?, rhs?): return try areInIncreasingOrder(lhs, rhs)
case (.none, _): return false
case (_, .none): return true
}
})
}
}
Usage:
array.sort(\.optionalStringProperty) {
$0.localizedStandardCompare($1) == .orderedAscending
}
print(array)

Counting letter occurrences in a String Swift

Found a cute way of counting the occurrences of character in a String:
let inputString = "test this string"
var frequencies : [Character: Int] = [:]
let baseCounts = zip(
inputString, repeatElement(1,count: Int.max))
frequencies = Dictionary(baseCounts, uniquingKeysWith: +)
with the result
["i": 2, "r": 1, "n": 1, "e": 1, "s": 3, " ": 2, "g": 1, "t": 4, "h": 1]
However I tried to use a range for the elements such that
let secondBaseCounts = zip(inputString, 0...)
frequencies = Dictionary(secondBaseCounts, uniquingKeysWith: +)
but get the incorrect result:
["i": 20, "r": 12, "n": 14, "e": 1, "s": 20, " ": 13, "g": 15, "t": 19, "h": 6]
Why?
Your second attempt doesn't implement what you meant to implement. zip(inputString, 0...) simply maps the Int index of each character to the character itself.
So the value of secondBaseCounts will be
["t", 0), ("e", 1), ("s", 2), ("t", 3), (" ", 4), ("t", 5), ("h", 6), ("i", 7), ("s", 8), (" ", 9), ("s", 10), ("t", 11), ("r", 12), ("i", 13), ("n", 14), ("g", 15)]
Calling Dictionary(secondBaseCounts, uniquingKeysWith: +) sums each value associated with repeating keys, meaning that the values in your final frequencies dictionary will be the sum of all indices where a certain character occurs in inputString rather than the count of occurrences of that character.
I am answering this question without using any Build-In methods. Most likely to be asked in Interviews.
var inputString = "test this string"
extension String {
func countCharacterOccurances() -> Dictionary<String, Any> {
var occuranceDict : [String : Int] = [:]
for i in self {
var count = 1
if occuranceDict[String(i)] == nil {
occuranceDict[String(i)] = count
}
else {
count = occuranceDict[String(i)] ?? 0
count += 1
occuranceDict[String(i)] = count
}
}
return occuranceDict as Dictionary<String, Any>
}
}
var characterOccuranceDict = inputString.countCharacterOccurances()
Output : ["h": 1, "t": 4, "i": 2, "g": 1, "s": 3, "r": 1, " ": 2, "n": 1, "e": 1]
The following code is useful if you want a dictionary that is sorted alphabetically by Keys. Otherwise, you can print characterOccuranceDict.
Problem statement:- "write a program to count consecutive characters in the string and combine them in a string in given order"
let sortedDictionary = characterOccuranceDict.sorted { $0.key < $1.key }
sortedDictionary.forEach { (item) in
finalStr = finalStr + item.key + String(describing: item.value)
}
print(finalStr)
inputString = "AAABBCCCCD"
Output : A3B2C4D1
swift ios

Get all key combinations from nested maps

I have a nested map like so:
val m: Map[Int, Map[String, Seq[Int]]] =
Map(
1 -> Map(
"A" -> Seq(1, 2, 3),
"B" -> Seq(4, 5, 6)
),
2 -> Map(
"C" -> Seq(7, 8, 9),
"D" -> Seq(10, 11, 12),
"E" -> Seq(13, 14, 15)
),
3 -> Map(
"F" -> Seq(16, 17, 18)
)
)
I want the desired output to show every possible combination of the integers in the Seqs. For example:
List((1, "A", 1),
(1, "A", 2),
(1, "A", 3),
(1, "B", 4),
(1, "B", 5),
(1, "B", 6),
(2, "C", 7),
(2, "C", 8),
(2, "C", 9),
(2, "D", 10),
(2, "D", 11),
(2, "D", 12),
(2, "E", 13),
(2, "E", 14),
(2, "E", 15),
(3, "F", 16),
(3, "F", 17),
(3, "F", 18))
I have been trying different combinations of map and flatMap, but nothing has been working. Any ideas?
Here is a possibility using a for comprehension:
for {
(k1, v1) <- m
(k2, v2) <- v1
v3 <- v2
} yield (k1, k2, v3)
This goes through all top key/value pairs of m. For each of these top values, this goes through all nested key/values. And finally for all of these nested values (which are the lists), it goes through each elements and yields what's requested.
A for comprehension is an equivalent to nested flatMaps, such as:
m.flatMap{
case (k1, v1) => v1.flatMap {
case (k2, v2) => v2.map(v3 => (k1, k2, v3))
}
}

Merge multiple value in Json

I'm working with this kind of Json in Scala :
{
"ClientBase": [
{
"string#name": "robert",
"int#age": 46,
"string#country": "USA"
},
{
"string#name": "tom",
"int#age": 45,
"string#country": "UK"
}
]
}
I use Json4s library and I would like to add a new field to each client. I know how do this for one but is there a quick way to do this for every one ?
I would like a result like this :
{
"ClientBase": [
{
"string#name": "robert",
"int#age": 46,
"string#country": "USA",
"BLOCK_ID" : "client_base"
},
{
"string#name": "tom",
"int#age": 45,
"string#country": "UK",
"BLOCK_ID" : "client_base"
}
]
}
Can you not just map over them all using your Json -> Json function that adds it to one? Something like:
val withBlock = parse(withoutBlock).extract[List[Clients]] map addBlock
Or am I not understanding the question?
Here is an approach using scala.util.parsing.json
import scala.util.parsing.json.JSON
val string =
"""{
| "ClientBase": [
| {
| "string#name": "robert",
| "int#age": 46,
| "string#country": "USA"
| },
| {
| "string#name": "tom",
| "int#age": 45,
| "string#country": "UK"
| }
| ]
|}
""".stripMargin
val start = """"{"ClientBase":[{"""
val end = """}]}"""
val json = JSON.parseFull(string) match {
case Some(e) =>
val clientBase = e.asInstanceOf[Map[String,Any]]
.getOrElse("ClientBase", List[Map[String,Any]]())
val list = clientBase.asInstanceOf[List[Map[String, Any]]]
val result = list.map(e=> e.+("BLOCK_ID" -> "client_base"))
result.mkString(start, ",", end)
case None => string
}
print(json)
//"{"ClientBase":[{Map(string#name -> robert, int#age -> 46.0, string#country -> USA, BLOCK_ID -> client_base),Map(string#name -> tom, int#age -> 45.0, string#country -> UK, BLOCK_ID -> client_base)}]}
Hope this is a helpful.