// -------- auxilliary functions --------
def printLayout
(layout
: List
[List
[String
]]): Unit
= println((layout ++ List(List())).map(_.mkString).mkString("|\n").init)
def renderCentered
(txt
: String, width
: Int
): String
= { val rem
= width - txt.
size val (lSpan, rSpan
) = (rem /
2,
(rem +
1) /
2) "|" + " " * (lSpan - 1) + txt + " " * rSpan
}
// -------- data model --------
def render
(width
: Int
): String
}
name: String,
price
: BigDecimal
) extends Renderable
{ def render
(width
: Int
): String
= renderCentered
(s
"$id,$name,$price", width
) }
def render
(width
: Int
): String
= renderCentered
(phType, width
) }
case class LayoutRules
(columnPattern
: List
[Int
],
placeholderPlacement: Map[String, PlaceholderRule],
placeholdersDensity: Double = 1.0)
minSkipRows: Int)
// -------- rendering implementation --------
val rand
= new scala.
util.
Random
def renderLayout
(products
: List
[Product
],
placeholders: List[Placeholder],
rules: LayoutRules,
maxWidth: Int = 60): List[List[String]] = {
val itemsPerCycle
= rules.
columnPattern.
sum
def renderAux
(products
: List
[Product
],
groupedPHs: Map[String, List[Placeholder]],
columnPattern: List[Int],
placeholderStats: Map[String, Int],
acc: List[List[String]]): List[List[String]] = {
val remainingPlaceholders
= groupedPHs.
map(_.
_2.
size).
sum val remainingItems
= products.
size + remainingPlaceholders
val estimatedFullCycles
= remainingItems / itemsPerCycle
val lastCycleItems
= remainingItems - estimatedFullCycles
* itemsPerCycle
val lastCycleRows
= if(lastCycleItems
== 0) 0 else rules.
columnPattern .scanLeft(0)(_ + _).tail
.dropWhile(_ < lastCycleItems).size + 1
val estimatedRemainingRows
= estimatedFullCycles / columnPattern.
size + lastCycleRows
// TODO: if some PHs are available and last row does not match to the column
// TODO: pattern, then we can add remaining PHs
acc
case prods
if rand.
nextBoolean() => // putting placeholder in this row, if possible val curRowCols
= columnPattern.
head val (prodsRow, prodsRest
) = prods.
splitAt(curRowCols
) val nextColPattern
= columnPattern.
tail :+ curRowCols
val curRow
= prodsRow.
map(_.
render(maxWidth / prodsRow.
size)) renderAux(prodsRest, groupedPHs, nextColPattern, placeholderStats, curRow :: acc)
case prods
=> // no placeholder in this row val curRowCols
= columnPattern.
head val (prodsRow, prodsRest
) = prods.
splitAt(curRowCols
) val nextColPattern
= columnPattern.
tail :+ curRowCols
val curRow
= prodsRow.
map(_.
render(maxWidth / prodsRow.
size)) renderAux(prodsRest, groupedPHs, nextColPattern, placeholderStats, curRow :: acc)
}
}
val groupedByType
= placeholders.
groupBy(_.
phType)
renderAux(products, groupedByType, rules.columnPattern, Map(), Nil).reverse
}
// -------- example usage --------
val products
= (1 to
28).
map(i
=>Product
(s
"$i", s
"name $i", i
* 42)).
toList (1 to 3).map(_ => NeutralPH)
++ (1 to 2).map(_ => ProductInfoPH)
++ (1 to 4).map(_ => SocialMediaPH)
++ (1 to 1).map(_ => SellingDriverPH)
).toList
columnPattern = List(1,2,3),
placeholderPlacement = Map(
NeutralPH.phType -> PlaceholderRule(maxPerRow = 1, minSkipRows = 4),
ProductInfoPH.phType -> PlaceholderRule(maxPerRow = 2, minSkipRows = 3),
SocialMediaPH.phType -> PlaceholderRule(maxPerRow = 1, minSkipRows = 5),
SellingDriverPH.phType -> PlaceholderRule(maxPerRow = 1, minSkipRows = 4)
)
)
printLayout(renderLayout(products, placeholders, rules))
// your code goes here
}
b2JqZWN0IE1haW4gZXh0ZW5kcyBBcHAgewoJCgkvLyAtLS0tLS0tLSBhdXhpbGxpYXJ5IGZ1bmN0aW9ucyAtLS0tLS0tLQpkZWYgcHJpbnRMYXlvdXQobGF5b3V0OiBMaXN0W0xpc3RbU3RyaW5nXV0pOiBVbml0ID0KICBwcmludGxuKChsYXlvdXQgKysgTGlzdChMaXN0KCkpKS5tYXAoXy5ta1N0cmluZykubWtTdHJpbmcoInxcbiIpLmluaXQpCmRlZiByZW5kZXJDZW50ZXJlZCh0eHQ6IFN0cmluZywgd2lkdGg6IEludCk6IFN0cmluZyA9IHsKICB2YWwgcmVtID0gd2lkdGggLSB0eHQuc2l6ZQogIHZhbCAobFNwYW4sIHJTcGFuKSA9IChyZW0gLyAyLCAocmVtICsgMSkgLyAyKQogICJ8IiArICIgIiAqIChsU3BhbiAtIDEpICsgdHh0ICsgIiAiICogclNwYW4KfQovLyAtLS0tLS0tLSBkYXRhIG1vZGVsIC0tLS0tLS0tCnRyYWl0IFJlbmRlcmFibGUgewogIGRlZiByZW5kZXIod2lkdGg6IEludCk6IFN0cmluZwp9CmNhc2UgY2xhc3MgUHJvZHVjdChpZDogU3RyaW5nLAogICAgICAgICAgICAgICAgICAgbmFtZTogU3RyaW5nLAogICAgICAgICAgICAgICAgICAgcHJpY2U6IEJpZ0RlY2ltYWwpIGV4dGVuZHMgUmVuZGVyYWJsZSB7CiAgZGVmIHJlbmRlcih3aWR0aDogSW50KTogU3RyaW5nID0gcmVuZGVyQ2VudGVyZWQocyIkaWQsJG5hbWUsJHByaWNlIiwgd2lkdGgpCn0KdHJhaXQgUGxhY2Vob2xkZXIgZXh0ZW5kcyBSZW5kZXJhYmxlIHsKICBkZWYgcGhUeXBlOiBTdHJpbmcKICBkZWYgcmVuZGVyKHdpZHRoOiBJbnQpOiBTdHJpbmcgPSByZW5kZXJDZW50ZXJlZChwaFR5cGUsIHdpZHRoKQp9CmNhc2Ugb2JqZWN0IE5ldXRyYWxQSCBleHRlbmRzIFBsYWNlaG9sZGVyIHsgdmFsIHBoVHlwZSA9ICJuZXV0cmFsIiB9CmNhc2Ugb2JqZWN0IFByb2R1Y3RJbmZvUEggZXh0ZW5kcyBQbGFjZWhvbGRlciB7IHZhbCBwaFR5cGUgPSAicHJvZHVjdCBpbmZvIiB9CmNhc2Ugb2JqZWN0IFNvY2lhbE1lZGlhUEggZXh0ZW5kcyBQbGFjZWhvbGRlciB7IHZhbCBwaFR5cGUgPSAic20iIH0KY2FzZSBvYmplY3QgU2VsbGluZ0RyaXZlclBIIGV4dGVuZHMgUGxhY2Vob2xkZXIgeyB2YWwgcGhUeXBlID0gInNlbGxpbmciIH0KY2FzZSBjbGFzcyBMYXlvdXRSdWxlcyhjb2x1bW5QYXR0ZXJuOiBMaXN0W0ludF0sCiAgICAgICAgICAgICAgICAgICAgICAgcGxhY2Vob2xkZXJQbGFjZW1lbnQ6IE1hcFtTdHJpbmcsIFBsYWNlaG9sZGVyUnVsZV0sCiAgICAgICAgICAgICAgICAgICAgICAgcGxhY2Vob2xkZXJzRGVuc2l0eTogRG91YmxlID0gMS4wKQpjYXNlIGNsYXNzIFBsYWNlaG9sZGVyUnVsZShtYXhQZXJSb3c6IEludCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluU2tpcFJvd3M6IEludCkKCi8vIC0tLS0tLS0tIHJlbmRlcmluZyBpbXBsZW1lbnRhdGlvbiAtLS0tLS0tLQp2YWwgcmFuZCA9IG5ldyBzY2FsYS51dGlsLlJhbmRvbQoKZGVmIHJlbmRlckxheW91dChwcm9kdWN0czogTGlzdFtQcm9kdWN0XSwKICAgICAgICAgICAgICAgICBwbGFjZWhvbGRlcnM6IExpc3RbUGxhY2Vob2xkZXJdLAogICAgICAgICAgICAgICAgIHJ1bGVzOiBMYXlvdXRSdWxlcywKICAgICAgICAgICAgICAgICBtYXhXaWR0aDogSW50ID0gNjApOiBMaXN0W0xpc3RbU3RyaW5nXV0gPSB7CgogIHZhbCBpdGVtc1BlckN5Y2xlID0gcnVsZXMuY29sdW1uUGF0dGVybi5zdW0KCgogIGRlZiByZW5kZXJBdXgocHJvZHVjdHM6IExpc3RbUHJvZHVjdF0sCiAgICAgICAgICAgICAgICBncm91cGVkUEhzOiBNYXBbU3RyaW5nLCBMaXN0W1BsYWNlaG9sZGVyXV0sCiAgICAgICAgICAgICAgICBjb2x1bW5QYXR0ZXJuOiBMaXN0W0ludF0sCiAgICAgICAgICAgICAgICBwbGFjZWhvbGRlclN0YXRzOiBNYXBbU3RyaW5nLCBJbnRdLAogICAgICAgICAgICAgICAgYWNjOiBMaXN0W0xpc3RbU3RyaW5nXV0pOiBMaXN0W0xpc3RbU3RyaW5nXV0gPSB7CgogICAgdmFsIHJlbWFpbmluZ1BsYWNlaG9sZGVycyA9IGdyb3VwZWRQSHMubWFwKF8uXzIuc2l6ZSkuc3VtCiAgICB2YWwgcmVtYWluaW5nSXRlbXMgPSBwcm9kdWN0cy5zaXplICsgcmVtYWluaW5nUGxhY2Vob2xkZXJzCiAgICB2YWwgZXN0aW1hdGVkRnVsbEN5Y2xlcyA9IHJlbWFpbmluZ0l0ZW1zIC8gaXRlbXNQZXJDeWNsZQogICAgdmFsIGxhc3RDeWNsZUl0ZW1zID0gcmVtYWluaW5nSXRlbXMgLSBlc3RpbWF0ZWRGdWxsQ3ljbGVzICogaXRlbXNQZXJDeWNsZQogICAgdmFsIGxhc3RDeWNsZVJvd3MgPSBpZihsYXN0Q3ljbGVJdGVtcyA9PSAwKSAwIGVsc2UgcnVsZXMuY29sdW1uUGF0dGVybgogICAgICAgIC5zY2FuTGVmdCgwKShfICsgXykudGFpbAogICAgICAgIC5kcm9wV2hpbGUoXyA8IGxhc3RDeWNsZUl0ZW1zKS5zaXplICsgMQogICAgdmFsIGVzdGltYXRlZFJlbWFpbmluZ1Jvd3MgPSBlc3RpbWF0ZWRGdWxsQ3ljbGVzIC8gY29sdW1uUGF0dGVybi5zaXplICsgbGFzdEN5Y2xlUm93cwoKICAgIHByb2R1Y3RzIG1hdGNoIHsKICAgICAgY2FzZSBOaWwgPT4KICAgICAgICAvLyBUT0RPOiBpZiBzb21lIFBIcyBhcmUgYXZhaWxhYmxlIGFuZCBsYXN0IHJvdyBkb2VzIG5vdCBtYXRjaCB0byB0aGUgY29sdW1uCiAgICAgICAgLy8gVE9ETzogcGF0dGVybiwgdGhlbiB3ZSBjYW4gYWRkIHJlbWFpbmluZyBQSHMKICAgICAgICBhY2MKICAgICAgY2FzZSBwcm9kcyBpZiByYW5kLm5leHRCb29sZWFuKCkgPT4gLy8gcHV0dGluZyBwbGFjZWhvbGRlciBpbiB0aGlzIHJvdywgaWYgcG9zc2libGUKICAgICAgICB2YWwgY3VyUm93Q29scyA9IGNvbHVtblBhdHRlcm4uaGVhZAogICAgICAgIHZhbCAocHJvZHNSb3csIHByb2RzUmVzdCkgPSBwcm9kcy5zcGxpdEF0KGN1clJvd0NvbHMpCiAgICAgICAgdmFsIG5leHRDb2xQYXR0ZXJuID0gY29sdW1uUGF0dGVybi50YWlsIDorIGN1clJvd0NvbHMKICAgICAgICB2YWwgY3VyUm93ID0gcHJvZHNSb3cubWFwKF8ucmVuZGVyKG1heFdpZHRoIC8gcHJvZHNSb3cuc2l6ZSkpCiAgICAgICAgcmVuZGVyQXV4KHByb2RzUmVzdCwgZ3JvdXBlZFBIcywgbmV4dENvbFBhdHRlcm4sIHBsYWNlaG9sZGVyU3RhdHMsIGN1clJvdyA6OiBhY2MpCiAgICAgIGNhc2UgcHJvZHMgPT4gLy8gbm8gcGxhY2Vob2xkZXIgaW4gdGhpcyByb3cKICAgICAgICB2YWwgY3VyUm93Q29scyA9IGNvbHVtblBhdHRlcm4uaGVhZAogICAgICAgIHZhbCAocHJvZHNSb3csIHByb2RzUmVzdCkgPSBwcm9kcy5zcGxpdEF0KGN1clJvd0NvbHMpCiAgICAgICAgdmFsIG5leHRDb2xQYXR0ZXJuID0gY29sdW1uUGF0dGVybi50YWlsIDorIGN1clJvd0NvbHMKICAgICAgICB2YWwgY3VyUm93ID0gcHJvZHNSb3cubWFwKF8ucmVuZGVyKG1heFdpZHRoIC8gcHJvZHNSb3cuc2l6ZSkpCiAgICAgICAgcmVuZGVyQXV4KHByb2RzUmVzdCwgZ3JvdXBlZFBIcywgbmV4dENvbFBhdHRlcm4sIHBsYWNlaG9sZGVyU3RhdHMsIGN1clJvdyA6OiBhY2MpCiAgICB9CiAgfQoKICB2YWwgZ3JvdXBlZEJ5VHlwZSA9IHBsYWNlaG9sZGVycy5ncm91cEJ5KF8ucGhUeXBlKQoKICByZW5kZXJBdXgocHJvZHVjdHMsIGdyb3VwZWRCeVR5cGUsIHJ1bGVzLmNvbHVtblBhdHRlcm4sIE1hcCgpLCBOaWwpLnJldmVyc2UKfQovLyAtLS0tLS0tLSBleGFtcGxlIHVzYWdlIC0tLS0tLS0tCnZhbCBwcm9kdWN0cyA9ICgxIHRvIDI4KS5tYXAoaT0+UHJvZHVjdChzIiRpIiwgcyJuYW1lICRpIiwgaSAqIDQyKSkudG9MaXN0CnZhbCBwbGFjZWhvbGRlcnMgPSAoCiAgKDEgdG8gMykubWFwKF8gPT4gTmV1dHJhbFBIKQogICAgKysgKDEgdG8gMikubWFwKF8gPT4gUHJvZHVjdEluZm9QSCkKICAgICsrICgxIHRvIDQpLm1hcChfID0+IFNvY2lhbE1lZGlhUEgpCiAgICArKyAoMSB0byAxKS5tYXAoXyA9PiBTZWxsaW5nRHJpdmVyUEgpCikudG9MaXN0CnZhbCBydWxlcyA9IExheW91dFJ1bGVzKAogIGNvbHVtblBhdHRlcm4gPSBMaXN0KDEsMiwzKSwKICBwbGFjZWhvbGRlclBsYWNlbWVudCA9IE1hcCgKICAgIE5ldXRyYWxQSC5waFR5cGUgLT4gUGxhY2Vob2xkZXJSdWxlKG1heFBlclJvdyA9IDEsIG1pblNraXBSb3dzID0gNCksCiAgICBQcm9kdWN0SW5mb1BILnBoVHlwZSAtPiBQbGFjZWhvbGRlclJ1bGUobWF4UGVyUm93ID0gMiwgbWluU2tpcFJvd3MgPSAzKSwKICAgIFNvY2lhbE1lZGlhUEgucGhUeXBlIC0+IFBsYWNlaG9sZGVyUnVsZShtYXhQZXJSb3cgPSAxLCBtaW5Ta2lwUm93cyA9IDUpLAogICAgU2VsbGluZ0RyaXZlclBILnBoVHlwZSAtPiBQbGFjZWhvbGRlclJ1bGUobWF4UGVyUm93ID0gMSwgbWluU2tpcFJvd3MgPSA0KQogICkKKQoKcHJpbnRMYXlvdXQocmVuZGVyTGF5b3V0KHByb2R1Y3RzLCBwbGFjZWhvbGRlcnMsIHJ1bGVzKSkKCgoJCgkKCS8vIHlvdXIgY29kZSBnb2VzIGhlcmUKfQ==
| 1,name 1,42 |
| 2,name 2,84 | 3,name 3,126 |
| 4,name 4,168 | 5,name 5,210 | 6,name 6,252 |
| 7,name 7,294 |
| 8,name 8,336 | 9,name 9,378 |
| 10,name 10,420 | 11,name 11,462 | 12,name 12,504 |
| 13,name 13,546 |
| 14,name 14,588 | 15,name 15,630 |
| 16,name 16,672 | 17,name 17,714 | 18,name 18,756 |
| 19,name 19,798 |
| 20,name 20,840 | 21,name 21,882 |
| 22,name 22,924 | 23,name 23,966 | 24,name 24,1008 |
| 25,name 25,1050 |
| 26,name 26,1092 | 27,name 27,1134 |
| 28,name 28,1176 |