Group list by given occurrence in Scala - list

I have a list of strings that I'm trying to split into separate lists sequentially, grouping the 4th occurrence i.e. this list:
val data = List("1", "2", "3", "4", "5", "6", "7", "8")
should be grouped as
val list1 = List("1", "5")
val list2 = List("2", "6")
val list3 = List("3", "7")
val list4 = List("4", "8")
I'm not sure if I am trying to overcomplicate this but the only way I can think is to first group the elements using sliding e.g.:
data.sliding(4,4).toList
results in
List(List(1, 2, 3, 4), List(5, 6, 7, 8))
and then to implement my own unzip method that would group the above as my desired output.
Please can someone let me know if there is an easier way of doing this?

You can use .transpose on the list .sliding generates:
scala> val data = List("1", "2", "3", "4", "5", "6", "7", "8")
data: List[String] = List(1, 2, 3, 4, 5, 6, 7, 8)
scala> data.sliding(4, 4).toList
res1: List[List[String]] = List(List(1, 2, 3, 4), List(5, 6, 7, 8))
scala> data.sliding(4, 4).toList.transpose
res2: List[List[String]] = List(List(1, 5), List(2, 6), List(3, 7), List(4, 8))

A version which will work for every list length:
def groupNth[A](n: Int, list: List[A]): List[List[A]] = {
val (firstN, rest) = list.splitAt(n)
val groupedRest = if (rest.nonEmpty) groupNth(n, rest) else Nil
// null.asInstanceOf[A] is of course cheating, but the value is never used
firstN.zipAll(groupedRest, null.asInstanceOf[A], Nil).map {
case (h, t) => h :: t
}
}
println(groupNth(4, Nil))
// List()
println(groupNth(4, List(1, 2, 3)))
// List(List(1), List(2), List(3))
println(groupNth(4, List(1, 2, 3, 4, 5, 6, 7, 8)))
// List(List(1, 5), List(2, 6), List(3, 7), List(4, 8))
println(groupNth(4, List(1, 2, 3, 4, 5, 6, 7, 8, 9)))
// List(List(1, 5, 9), List(2, 6), List(3, 7), List(4, 8))
println(groupNth(4, List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)))
// List(List(1, 5, 9), List(2, 6, 10), List(3, 7, 11), List(4, 8, 12))

zip after sliding
scala> val data = List("1", "2", "3", "4", "5", "6", "7", "8")
data: List[String] = List("1", "2", "3", "4", "5", "6", "7", "8")
scala> val result = data.sliding(4, 4).toList
result: List[List[String]] = List(List("1", "2", "3", "4"), List("5", "6", "7", "8"))
scala> result.transpose
res7: List[(String, String)] = List(("1", "5"), ("2", "6"), ("3", "7"), ("4", "8"))

If tuples would do as output, it's fairly neat:
val tuples = data zip data.drop(4)
//> tuples : List[(String, String)] = List((1,5), (2,6), (3,7), (4,8))
turn them into List:
tuples.map{case(a,b) => List(a, b)}
//> List[List[String]] = List(List(1, 5), List(2, 6), List(3, 7), List(4, 8))
EDIT: Showing that the comment about only working with 8 is incorrect
def pairs[A](xs:List[A], n:Int) =
(xs zip xs.drop(n)).map{case(a,b) => List(a, b)}
pairs(List("1","2", "3", "4", "5", "6", "7", "8"), 4)
// List(List(1, 5), List(2, 6), List(3, 7), List(4, 8))
pairs(List("1","2", "3", "4", "5", "6", "7", "8", "9"), 4)
// List(List(1, 5), List(2, 6), List(3, 7), List(4, 8), List(5, 9))
pairs(List("1","2", "3", "4", "5", "6", "7", "8", "9", "10"), 4)
// List(List(1, 5), List(2, 6), List(3, 7), List(4, 8), List(5, 9), List(6, 10))
pairs(List("1","2", "3", "4"), 4)
// List()
pairs(List("1","2", "3"), 4)
// List()

Related

Merging a list of dictionaries

I have a list of dicts as below.
list = [ {id: 1, s_id:2, class: 'a', teacher: 'b'} ]
list1 = [ {id: 1, c_id:1, rank:2, area: 34}, {id:1, c_id:2, rank:1, area: 21} ]
I want to merge the two lists on the common key-value pairs (in this case 'id:1')
Merged_list = [ {id:1, s_id:2, class: 'a', teacher: 'b', list1: {c_id:1, rank: 2, area: 34}, {c_id:2, rank: 1, area: 21} ]
How do I go about this?
Thanks
You can use
merged_list = [{**d1, **d2} for d1, d2 in zip(list1, list2)]
>>> merged_list
[{'id': 1, 's_id': 2, 'class': 'a', 'teacher': 'b', 'rank': 2, 'area': 34},
{'id': 2, 's_id': 3, 'class': 'c', 'teacher': 'd', 'rank': 1, 'area': 21}]
where {**d1, **d2} is just a neat way to combine 2 dictionaries. Keep in mind this will replace the duplicate keys of the first dictionary. If you're on Python 3.9, you could use d1 | d2.
EDIT: For the edit in your question, you can try this horrible one liner (keep in mind this will create the pair list1: [] if no matching indeces were found on list1):
list_ = [{"id": 1, "s_id": 2, "class": 'a', "teacher": 'b'}]
list1 = [{"id": 1, "c_id": 1, "rank": 2, "area": 34}, {"id": 1, "c_id": 2, "rank": 1, "area": 21}]
merged_list = [{**d, **{"list1": [{k: v for k, v in d1.items() if k != "id"} for d1 in list1 if d1["id"] == d["id"]]}} for d in list_]
>>> merged_list
[{'id': 1,
's_id': 2,
'class': 'a',
'teacher': 'b',
'list1': [{'c_id': 1, 'rank': 2, 'area': 34},
{'c_id': 2, 'rank': 1, 'area': 21}]}]
This is equivalent to (with some added benefits):
merged_list = []
for d in list_:
matched_ids = []
for d1 in list1:
if d["id"] == d1["id"]:
d1.pop("id") # remove id from dictionary before appending
matched_ids.append(d1)
if matched_ids: # added benefit of not showing the 'list1' key if matched_ids is empty
found = {"list1": matched_ids}
else:
found = {}
merged_list.append({**d, **found})
Try this
And don't forget to put " " when declare string
list = [ {"id": 1, "s_id": 2 ," class": 'a', "teacher": 'b'}, {"id": 2, "s_id" : 3, "class" : 'c', "teacher": 'd'} ]
list1 = [ {"id": 1, "rank" :2, "area" : 34}, {"id" :2, "rank" :1, "area": 21} ]
list2 = list1 + list
print(list2)

Remove elements from List that are missing in another list

I would like to remove from List a elements that are not in another List. Suppose I have
List a = [{"id": 1}, {"id": 2}, {"id": 4}, {"id": 7},]
List b = [{"id": 1}, {"id": 2}, {"id": 7},]
Then I would like to remove from List a the element {"id": 4}, since it is missing in List b
If List b were
List b = [{"id": 1}, {"id": 2}, {"id": 4},]
then I would like to remove from List a element {"id": 7}
How to do this in Flutter.
Thank you.
void main() {
var a = [{"id": 1}, {"id": 2}, {"id": 4}, {"id": 7}];
var b = [{"id": 1}, {"id": 2}, {"id": 7}];
var c = a.map((m) => m['id']);
var d = b.map((m) => m['id']);
var e = c.toSet().difference(d.toSet());
var f = a.where((m) => e.contains(m['id']));
print(f);
}
Result
({id: 4})
I fixed it like this
List a = [{"id": 1}, {"id": 2}, {"id": 4}, {"id": 7}];
List b = [{"id": 1}, {"id": 2}, {"id": 7}];
a.removeWhere((elementA) => b.every((elementB) =>
elementB["id"] != elementA["id"]));

Django ORM queryset equivalent to group by year-month?

I have an Django app and need some datavisualization and I am blocked with ORM.
I have a models Orders with a field created_at and I want to present data with a diagram bar (number / year-month) in a dashboard template.
So I need to aggregate/annotate data from my model but did find a complete solution.
I find partial answer with TruncMonth and read about serializers but wonder if there is a simpliest solution with Django ORM possibilities...
In Postgresql it would be:
SELECT date_trunc('month',created_at), count(order_id) FROM "Orders" GROUP BY date_trunc('month',created_at) ORDER BY date_trunc('month',created_at);
"2021-01-01 00:00:00+01" "2"
"2021-02-01 00:00:00+01" "3"
"2021-03-01 00:00:00+01" "3"
...
example
1 "2021-01-04 07:42:03+01"
2 "2021-01-24 13:59:44+01"
3 "2021-02-06 03:29:11+01"
4 "2021-02-06 08:21:15+01"
5 "2021-02-13 10:38:36+01"
6 "2021-03-01 12:52:22+01"
7 "2021-03-06 08:04:28+01"
8 "2021-03-11 16:58:56+01"
9 "2022-03-25 21:40:10+01"
10 "2022-04-04 02:12:29+02"
11 "2022-04-13 08:24:23+02"
12 "2022-05-08 06:48:25+02"
13 "2022-05-19 15:40:12+02"
14 "2022-06-01 11:29:36+02"
15 "2022-06-05 02:15:05+02"
16 "2022-06-05 03:08:22+02"
expected result
[
{
"year-month": "2021-01",
"number" : 2
},
{
"year-month": "2021-03",
"number" : 3
},
{
"year-month": "2021-03",
"number" : 3
},
{
"year-month": "2021-03",
"number" : 1
},
{
"year-month": "2021-04",
"number" : 2
},
{
"year-month": "2021-05",
"number" : 3
},
{
"year-month": "2021-06",
"number" : 3
},
]
I have done this but I am not able to order by date:
Orders.objects.annotate(month=TruncMonth('created_at')).values('month').annotate(number=Count('order_id')).values('month', 'number').order_by()
<SafeDeleteQueryset [
{'month': datetime.datetime(2022, 3, 1, 0, 0, tzinfo=<UTC>), 'number': 4},
{'month': datetime.datetime(2022, 6, 1, 0, 0, tzinfo=<UTC>), 'number': 2},
{'month': datetime.datetime(2022, 5, 1, 0, 0, tzinfo=<UTC>), 'number': 1},
{'month': datetime.datetime(2022, 1, 1, 0, 0, tzinfo=<UTC>), 'number': 5},
{'month': datetime.datetime(2021, 12, 1, 0, 0, tzinfo=<UTC>), 'number': 1},
{'month': datetime.datetime(2022, 7, 1, 0, 0, tzinfo=<UTC>), 'number': 1},
{'month': datetime.datetime(2021, 9, 1, 0, 0, tzinfo=<UTC>), 'number': 2},
'...(remaining elements truncated)...'
]>
Try adding the order_by on the original field if you have multi-year data.
from django.db.models import Sum
from django.db.models.functions import TruncMonth
Orders.objects.values(month=TruncMonth('created_at')).
order_by("created_at").annotate(Sum('number')

How to apply sum on a list of columns

I have a list var aggList : List[String]= List() the list contains the column names on which aggregation has to be applied.
I generate the dataframe as below:
var df = sc.parallelize(Seq[(Int, Int, String, Int, Int, Int)](
(1234, 1234, "PRM", 2, 1, 1),
(1235, 1234, "PRM", 1239, 2, 10),
(1246, 1234, "PRM", 1234, 5, 15),
(1247, 1234, "PRM", 1254, 20, 12),
(1246, 1234, "PRM", 1234, 5, 13),
(1246, 1234, "SEC", 1234, 7, 15),
(1249, 1234, "SEC", 1234, 20, 1),
(1248, 1234, "SEC", 1234, 2, 2))
).toDF("col1", "col2", "col3", "col4", "col5", "col6")
I need to do df.groupby(col1).agg(sum(aggList))
How do I achieve this?

Django count number of records per day

I'm using Django 2.0
I am preparing data to show on a graph in template. I want to fetch number of records per day.
This is what I'm doing
qs = self.get_queryset().\
extra({'date_created': "date(created)"}).\
values('date_created').\
annotate(item_count=Count('id'))
but, the output given is
[
{'date_created': datetime.date(2018, 5, 24), 'item_count': 1},
{'date_created': datetime.date(2018, 5, 24), 'item_count': 1},
{'date_created': datetime.date(2018, 5, 24), 'item_count': 1},
{'date_created': datetime.date(2018, 5, 24), 'item_count': 1},
{'date_created': datetime.date(2018, 5, 24), 'item_count': 1},
{'date_created': datetime.date(2018, 5, 24), 'item_count': 1},
{'date_created': datetime.date(2018, 5, 24), 'item_count': 1}
]
Here data is not grouped and same date is returning repeatedly with count as 1
Try using TruncDate function.
See that answer