QGIS Processing Script Corrupts Accented Characters - qgis
I am using a processing script to refactor fields for three different input layers and save the refactored results to numerous output types (.shp, .tab, .xlsx)
The input layers have string values that contain accents (French). However, the output layers end up corrupted.
I have looked at another post in which they talk about .cpg files and changing the default for OGR. I have done that but the data still comes out wrong, though it is different than if I didn't add the code to implicitly get OGR to work in utf-8.
My source layers are in UTF-8.
My output layers appear to be in UTF-8.
I am pasting my code below. Please note that I use the processing.run() to execute native:saveFeatures which I copied from the history for manually running the Processing Tool "Save Vector Features To File". When I run that tool, or the Refactor Fields tool, the output does have the correct accents in the data.
PLEASE HELP !
# -*- coding: utf-8 -*-
import os
# Section removed to adhere to stackOverflow char limit
# This section had all the import statements
orgEncoding=QgsSettings().value('/Processing/encoding') # save setting
QgsSettings().setValue('/Processing/encoding', 'utf-8') # set uft8
# Section removed to adhere to stackOverflow char limit
# This section had all the required QGIS processing script function calls
# This section also had the initAlgorithm
def processAlgorithm(self, parameters, context, feedback):
"""
Here is where the processing itself takes place.
"""
global renamer
sdcFileName = 'Eligible_Satellite_Dependent_Communities'
etcFileName = 'Eligible_Transport_Communities'
roadsFileName = 'Eligible_Roads'
sdcFileName_Fr = 'Collectivités_dépendantes_des_satellites_admissibles'
etcFileName_Fr = 'Collectivités_admissibles_sans_transport_à_grande_capacité'
roadsFileName_Fr = 'Routes_admissibles'
version = self.parameterAsString(
parameters,
self.VERSION,
context
)
# Retrieve the feature source and sink. The 'dest_id' variable is used
# to uniquely identify the feature sink, and must be included in the
# dictionary returned by the processAlgorithm function.
#----------------------------------------
sdcs = self.parameterAsSource(
parameters,
self.SDCS,
context
)
sdccommid = self.parameterAsString(
parameters,
self.SDCCOMMID,
context
)
sdcnameen = self.parameterAsString(
parameters,
self.SDCNAMEEN,
context
)
sdcnamefr = self.parameterAsString(
parameters,
self.SDCNAMEFR,
context
)
sdclat = self.parameterAsString(
parameters,
self.SDCLAT,
context
)
sdclon = self.parameterAsString(
parameters,
self.SDCLON,
context
)
sdcprovterr = self.parameterAsString(
parameters,
self.SDCPROVTERR,
context
)
#-----------------------------------------------------------------------
etcs = self.parameterAsSource(
parameters,
self.ETCS,
context
)
etccommid = self.parameterAsString(
parameters,
self.ETCCOMMID,
context
)
etcnameen = self.parameterAsString(
parameters,
self.ETCNAMEEN,
context
)
etcnamefr = self.parameterAsString(
parameters,
self.ETCNAMEFR,
context
)
etcprovterr = self.parameterAsString(
parameters,
self.ETCPROVTERR,
context
)
etcsatdep = self.parameterAsString(
parameters,
self.ETCSATDEP,
context
)
etclat = self.parameterAsString(
parameters,
self.ETCLAT,
context
)
etclon = self.parameterAsString(
parameters,
self.ETCLON,
context
)
#-----------------------------------------------------------------------
roads = self.parameterAsSource(
parameters,
self.ROADS,
context
)
roadid = self.parameterAsString(
parameters,
self.ROADID,
context
)
roadname = self.parameterAsString(
parameters,
self.ROADNAME,
context
)
roadrank = self.parameterAsString(
parameters,
self.ROADRANK,
context
)
roadclass = self.parameterAsString(
parameters,
self.ROADCLASS,
context
)
roadlengthkm = self.parameterAsString(
parameters,
self.ROADLENGTHKM,
context
)
#-----------------------------------------------------------------------
inputSAVEPATH = self.parameterAsString(
parameters,
self.INPUTSAVEPATH,
context
)
# Check to see if format specific directories exist. If not, create
if not (os.path.exists(inputSAVEPATH+"/EN/mapinfo")):
os.makedirs(inputSAVEPATH+"/EN/mapinfo")
if not (os.path.exists(inputSAVEPATH+"/EN/esri")):
os.makedirs(inputSAVEPATH+"/EN/esri")
if not (os.path.exists(inputSAVEPATH+"/FR/mapinfo")):
os.makedirs(inputSAVEPATH+"/FR/mapinfo")
if not (os.path.exists(inputSAVEPATH+"/FR/esri")):
os.makedirs(inputSAVEPATH+"/FR/esri")
if not (os.path.exists(inputSAVEPATH+"/EN/google")):
os.makedirs(inputSAVEPATH+"/EN/google")
if not (os.path.exists(inputSAVEPATH+"/FR/google")):
os.makedirs(inputSAVEPATH+"/FR/google")
# If source was not found, throw an exception to indicate that the algorithm
# encountered a fatal error. The exception text can be any string, but in this
# case we use the pre-built invalidSourceError method to return a standard
# helper text for when a source cannot be evaluated
if sdcs is None or etcs is None or roads is None:
raise QgsProcessingException(self.invalidSourceError(parameters, self.SDCS))
# Field types: applicable only to refactor fields
# 1 Boolean
# 2 Integer
# 4 Integer 64
# 6 Double
# 10 String
# 14 Date
# 16 DateTime
# REFACTOR Input files with standardized field names
print("***RUNNING REFACTOR***")
# ENGLISH SHORT
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
refsdc=processing.run("native:refactorfields", {
'INPUT':sdcs.sourceName(),
'FIELDS_MAPPING':[
{'expression': sdccommid,'length': 60,'name': 'CommID','precision': 0,'type': 4},
{'expression': sdcnameen,'length': 60,'name': 'Name_en','precision': 0,'type': 10},
{'expression': sdcnamefr,'length': 60,'name': 'Name_fr','precision': 0,'type': 10},
{'expression': sdcprovterr,'length': 5,'name': 'ProvTerr','precision': 0,'type': 10},
{'expression': sdclat,'length': 15,'name': 'Latitude','precision': 6,'type': 6},
{'expression': sdclon,'length': 15,'name': 'Longitude','precision': 6,'type': 6}],
'OUTPUT':inputSAVEPATH+'/sdc_en_short_'+version+'.tab'},
is_child_algorithm=True,
context=context)
refetc=processing.run("native:refactorfields", {
'INPUT':etcs.sourceName(),
'FIELDS_MAPPING':[
{'expression': etccommid,'length': 60,'name': 'CommID','precision': 0,'type': 4},
{'expression': etcnameen,'length': 60,'name': 'Name_en','precision': 0,'type': 10},
{'expression': etcnamefr,'length': 60,'name': 'Name_fr','precision': 0,'type': 10},
{'expression': etcsatdep,'length': 50,'name': 'SATDEP', 'precision':0, 'type': 10},
{'expression': etcprovterr,'length': 5,'name': 'ProvTerr','precision': 0,'type': 10},
{'expression': etclat,'length': 15,'name': 'Latitude','precision': 6,'type': 6},
{'expression': etclon,'length': 15,'name': 'Longitude','precision': 6,'type': 6}],
'OUTPUT':inputSAVEPATH+'/etc_en_short_'+version+'.tab'},
is_child_algorithm=True,
context=context)
refroads=processing.run("native:refactorfields",
{'INPUT':roads.sourceName(),
'FIELDS_MAPPING':[
{'expression': roadid, 'length': 60,'name': 'Road_ID', 'precision': 0,'type': 4},
{'expression': roadname, 'length': 60,'name': 'Road_Name', 'precision': 0,'type': 10},
{'expression': roadrank, 'length': 5, 'name': 'Rank', 'precision': 0,'type': 4},
{'expression': roadclass, 'length': 5, 'name': 'Class', 'precision': 0,'type': 4},
{'expression': roadlengthkm,'length': 15,'name': 'Length_km','precision': 2,'type': 6}],
'OUTPUT':inputSAVEPATH+'/roads_en_short_'+version+'.tab'},
is_child_algorithm=True,
context=context)
# ENGLISH LONG
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
refsdc_long=processing.run("native:refactorfields", {
'INPUT':sdcs.sourceName(),
'FIELDS_MAPPING':[
{'expression': sdccommid,'length': 60,'name': 'Eligible_Community_Number','precision': 0,'type': 4},
{'expression': sdcnameen,'length': 60,'name': 'Eligible_Community_Name_en','precision': 0,'type': 10},
{'expression': sdcnamefr,'length': 60,'name': 'Eligible_Community_Name_fr','precision': 0,'type': 10},
{'expression': sdcprovterr,'length': 5,'name': 'ProvTerr','precision': 0,'type': 10},
{'expression': sdclat,'length': 15,'name': 'Latitude','precision': 6,'type': 6},
{'expression': sdclon,'length': 15,'name': 'Longitude','precision': 6,'type': 6}],
'OUTPUT':inputSAVEPATH+'/sdcs_en_long_'+version+'.tab'},
is_child_algorithm=True,
context=context)
refetc_long=processing.run("native:refactorfields", {
'INPUT':etcs.sourceName(),
'FIELDS_MAPPING':[
{'expression': etccommid,'length': 60,'name': 'Eligible_Community_Number','precision': 0,'type': 4},
{'expression': etcnameen,'length': 60,'name': 'Eligible_Community_Name_en','precision': 0,'type': 10},
{'expression': etcnamefr,'length': 60,'name': 'Eligible_Community_Name_fr','precision': 0,'type': 10},
{'expression': etcsatdep,'length': 50,'name': 'SATDEP', 'precision':0, 'type': 10},
{'expression': etcprovterr,'length': 5,'name': 'ProvTerr','precision': 0,'type': 10},
{'expression': etclat,'length': 15,'name': 'Latitude','precision': 6,'type': 6},
{'expression': etclon,'length': 15,'name': 'Longitude','precision': 6,'type': 6}],
'OUTPUT':inputSAVEPATH+'/etcs_en_long_'+version+'.tab'},
is_child_algorithm=True,
context=context)
refroads_long=processing.run("native:refactorfields",
{'INPUT':roads.sourceName(),
'FIELDS_MAPPING':[
{'expression': roadid, 'length': 60,'name': 'Eligible_Road_ID', 'precision': 0,'type': 4},
{'expression': roadname, 'length': 60,'name': 'Road_Name', 'precision': 0,'type': 10},
{'expression': roadrank, 'length': 5, 'name': 'Rank', 'precision': 0,'type': 4},
{'expression': roadclass, 'length': 5, 'name': 'Class', 'precision': 0,'type': 4},
{'expression': roadlengthkm,'length': 15,'name': 'Length_km','precision': 2,'type': 6}],
'OUTPUT':inputSAVEPATH+'/roads_en_long_'+version+'.tab'},
is_child_algorithm=True,
context=context)
# FRENCH SHORT
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
refsdc_fr=processing.run("native:refactorfields", {
'INPUT':sdcs.sourceName(),
'FIELDS_MAPPING':[
{'expression': sdccommid,'length': 60,'name': 'IDCollect','precision': 0,'type': 4},
{'expression': sdcnameen,'length': 60,'name': 'Nom_en','precision': 0,'type': 10},
{'expression': sdcnamefr,'length': 60,'name': 'Nom_fr','precision': 0,'type': 10},
{'expression': sdcprovterr,'length': 5,'name': 'ProvTerr','precision': 0,'type': 10},
{'expression': sdclat,'length': 15,'name': 'Latitude','precision': 6,'type': 6},
{'expression': sdclon,'length': 15,'name': 'Longitude','precision': 6,'type': 6}],
'OUTPUT':inputSAVEPATH+'/sdcs_fr_short_'+version+'.tab'},
is_child_algorithm=True,
context=context)
refetc_fr=processing.run("native:refactorfields", {
'INPUT':etcs.sourceName(),
'FIELDS_MAPPING':[
{'expression': etccommid,'length': 60,'name': 'IDCollect','precision': 0,'type': 4},
{'expression': etcnameen,'length': 60,'name': 'Nom_en','precision': 0,'type': 10},
{'expression': etcnamefr,'length': 60,'name': 'Nom_fr','precision': 0,'type': 10},
{'expression': etcsatdep,'length': 50,'name': 'SATDEP', 'precision':0, 'type': 10},
{'expression': etcprovterr,'length': 5,'name': 'ProvTerr','precision': 0,'type': 10},
{'expression': etclat,'length': 15,'name': 'Latitude','precision': 6,'type': 6},
{'expression': etclon,'length': 15,'name': 'Longitude','precision': 6,'type': 6}],
'OUTPUT':inputSAVEPATH+'/etcs_fr_short_'+version+'.tab'},
is_child_algorithm=True,
context=context)
refroads_fr=processing.run("native:refactorfields",
{'INPUT':roads.sourceName(),
'FIELDS_MAPPING':[
{'expression': roadid, 'length': 60,'name': 'ID_Route', 'precision': 0,'type': 4},
{'expression': roadname, 'length': 60,'name': 'Nom_Route', 'precision': 0,'type': 10},
{'expression': roadrank, 'length': 5, 'name': 'Rang', 'precision': 0,'type': 4},
{'expression': roadclass, 'length': 5, 'name': 'Classe', 'precision': 0,'type': 4},
{'expression': roadlengthkm,'length': 15,'name': 'Longueur_k','precision': 2,'type': 6}],
'OUTPUT':inputSAVEPATH+'/roads_fr_short_'+version+'.tab'},
is_child_algorithm=True,
context=context)
# FRENCH LONG
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
refsdc_fr_long=processing.run("native:refactorfields", {
'INPUT':sdcs.sourceName(),
'FIELDS_MAPPING':[
{'expression': sdccommid,'length': 60,'name': 'Num_de_la_collectivité_admiss','precision': 0,'type': 4},
{'expression': sdcnameen,'length': 60,'name': 'Nom_de_la_collectivité_en','precision': 0,'type': 10},
{'expression': sdcnamefr,'length': 60,'name': 'Nom_de_la_collectivité_fr','precision': 0,'type': 10},
{'expression': sdcprovterr,'length': 5,'name': 'ProvTerr','precision': 0,'type': 10},
{'expression': sdclat,'length': 15,'name': 'Latitude','precision': 6,'type': 6},
{'expression': sdclon,'length': 15,'name': 'Longitude','precision': 6,'type': 6}],
'OUTPUT':inputSAVEPATH+'/sdcs_fr_long_'+version+'.tab'},
is_child_algorithm=True,
context=context)
refetc_fr_long=processing.run("native:refactorfields", {
'INPUT':etcs.sourceName(),
'FIELDS_MAPPING':[
{'expression': etccommid,'length': 60,'name': 'Num_de_la_collectivité_admiss','precision': 0,'type': 4},
{'expression': etcnameen,'length': 60,'name': 'Nom_de_la_collectivité_en','precision': 0,'type': 10},
{'expression': etcnamefr,'length': 60,'name': 'Nom_de_la_collectivité_fr','precision': 0,'type': 10},
{'expression': etcsatdep,'length': 50,'name': 'SATDEP', 'precision':0, 'type': 10},
{'expression': etcprovterr,'length': 5,'name': 'ProvTerr','precision': 0,'type': 10},
{'expression': etclat,'length': 15,'name': 'Latitude','precision': 6,'type': 6},
{'expression': etclon,'length': 15,'name': 'Longitude','precision': 6,'type': 6}],
'OUTPUT':inputSAVEPATH+'/etcs_fr_long_'+version+'.tab'},
is_child_algorithm=True,
context=context)
refroads_fr_long=processing.run("native:refactorfields",
{'INPUT':roads.sourceName(),
'FIELDS_MAPPING':[
{'expression': roadid, 'length': 60,'name': 'ID_de_la_route_admissible', 'precision': 0,'type': 4},
{'expression': roadname, 'length': 60,'name': 'Nom_de_la_route', 'precision': 0,'type': 10},
{'expression': roadrank, 'length': 5, 'name': 'Rang', 'precision': 0,'type': 4},
{'expression': roadclass, 'length': 5, 'name': 'Classe', 'precision': 0,'type': 4},
{'expression': roadlengthkm,'length': 15,'name': 'Longueur_km','precision': 2,'type': 6}],
'OUTPUT':inputSAVEPATH+'/roads_fr_long_'+version+'.tab'},
is_child_algorithm=True,
context=context)
# Create post-processing objects for renaming layers in layers pane
global sdcrenamer
global etcrenamer
global roadsrenamer
global sdcrenamer_fr
global etcrenamer_fr
global roadsrenamer_fr
sdcrenamer = Renamer(sdcFileName+'_'+version)
etcrenamer = Renamer(etcFileName+'_'+version)
roadsrenamer = Renamer(roadsFileName+'_'+version)
sdcrenamer_fr = Renamer(sdcFileName_Fr+'_'+version)
etcrenamer_fr = Renamer(etcFileName_Fr+'_'+version)
roadsrenamer_fr = Renamer(roadsFileName_Fr+'_'+version)
#context.layerToLoadOnCompletionDetails(refsdc['OUTPUT']).setPostProcessor(sdcrenamer)
#context.layerToLoadOnCompletionDetails(refetc['OUTPUT']).setPostProcessor(etcrenamer)
#context.layerToLoadOnCompletionDetails(refroads['OUTPUT']).setPostProcessor(roadsrenamer)
#context layerToLoadOnCompletionDetails(refsdc_fr['OUTPUT']).setPostProcessor(sdcrenamer_fr)
#context.layerToLoadOnCompletionDetails(refetc_fr['OUTPUT']).setPostProcessor(etcrenamer_fr)
#context.layerToLoadOnCompletionDetails(refroads_fr['OUTPUT']).setPostProcessor(roadsrenamer_fr)
print("***RUNNING SAVE XLSX***")
# Save refactored layers to .shp / .tab and / .xlsx formats
processing.run("native:savefeatures",
{'INPUT': refsdc['OUTPUT'],
'OUTPUT':inputSAVEPATH+'/Eligible_Satellite_Dependent_Communities_'+version+'.xlsx',
'LAYER_NAME':'Eligible_SDCs',
'DATASOURCE_OPTIONS':'overwrite',
'LAYER_OPTIONS':''})
processing.run("native:savefeatures",
{'INPUT': refetc['OUTPUT'],
'OUTPUT':inputSAVEPATH+'/Eligible_Transport_Communities_'+version+'.xlsx',
'LAYER_NAME':'Eligible_Communities',
'DATASOURCE_OPTIONS':'overwrite',
'LAYER_OPTIONS':''})
processing.run("native:savefeatures",
{'INPUT': refroads['OUTPUT'],
'OUTPUT':inputSAVEPATH+'/Eligible_Roads_'+version+'.xlsx',
'LAYER_NAME':'Eligible_Roads',
'DATASOURCE_OPTIONS':'overwrite',
'LAYER_OPTIONS':''})
#-----------------------------------------------------------------------
# MAPINFO FORMATS
processing.run("native:savefeatures",
{'INPUT': refsdc_long['OUTPUT'],
'OUTPUT':inputSAVEPATH+'/EN/mapinfo/Eligible_Satellite_Dependent_Communities_'+version+'.tab',
'LAYER_NAME':'Eligible_SDCs',
'DATASOURCE_OPTIONS':'overwrite',
'LAYER_OPTIONS':''})
processing.run("native:savefeatures",
{'INPUT': refetc_long['OUTPUT'],
'OUTPUT':inputSAVEPATH+'/EN/mapinfo/Eligible_Transport_Communities_'+version+'.tab',
'LAYER_NAME':'Eligible_Communities',
'DATASOURCE_OPTIONS':'overwrite',
'LAYER_OPTIONS':''})
processing.run("native:savefeatures",
{'INPUT': refroads_long['OUTPUT'],
'OUTPUT':inputSAVEPATH+'/EN/mapinfo/Eligible_Roads_'+version+'.tab',
'LAYER_NAME':'Eligible_Roads',
'DATASOURCE_OPTIONS':'overwrite',
'LAYER_OPTIONS':''})
processing.run("native:savefeatures",
{'INPUT': refsdc_fr_long['OUTPUT'],
'OUTPUT':inputSAVEPATH+'/FR/mapinfo/Collectivités_dépendantes_dun_satellite_admissibles_'+version+'.tab',
'LAYER_NAME':'Collectivités_dépendantes_dun_satellite_admissibles',
'DATASOURCE_OPTIONS':'overwrite',
'LAYER_OPTIONS':''})
processing.run("native:savefeatures",
{'INPUT': refetc_fr_long['OUTPUT'],
'OUTPUT':inputSAVEPATH+'/FR/mapinfo/Collectivités_admissibles_sans_transport_à_grande_capacité_'+version+'.tab',
'LAYER_NAME':'Collectivités_admissibles_sans_transport_à_grande_capacité',
'DATASOURCE_OPTIONS':'overwrite',
'LAYER_OPTIONS':''})
processing.run("native:savefeatures",
{'INPUT': refroads_fr_long['OUTPUT'],
'OUTPUT':inputSAVEPATH+'/FR/mapinfo/Routes_admissibles_'+version+'.tab',
'LAYER_NAME':'Routes_admissibles',
'DATASOURCE_OPTIONS':'overwrite',
'LAYER_OPTIONS':''})
#-----------------------------------------------------------------------
# SHAPEFILES
processing.run("native:savefeatures",
{'INPUT': refsdc['OUTPUT'],
'OUTPUT':inputSAVEPATH+'/EN/esri/Eligible_Satellite_Dependent_Communties_'+version+'.gpkg',
'LAYER_NAME':'Eligible_Satellite_Dependent_Communities',
'DATASOURCE_OPTIONS':'',
'LAYER_OPTIONS':''})
processing.run("native:savefeatures",
{'INPUT': refetc['OUTPUT'],
'OUTPUT':inputSAVEPATH+'/EN/esri/Eligible_Transport_Communities_'+version+'.gpkg',
'LAYER_NAME':'Eligible_Transport_Communities',
'DATASOURCE_OPTIONS':'',
'LAYER_OPTIONS':''})
processing.run("native:savefeatures",
{'INPUT': refroads['OUTPUT'],
'OUTPUT':inputSAVEPATH+'/EN/esri/Eligible_Roads_'+version+'.shp',
'LAYER_NAME':'Eligible_Roads',
'DATASOURCE_OPTIONS':'',
'LAYER_OPTIONS':''})
processing.run("native:savefeatures",
{'INPUT': refsdc_fr['OUTPUT'],
'OUTPUT':inputSAVEPATH+'/FR/esri/Collectivités_dépendantes_dun_satellite_admissibles_'+version+'.shp',
'LAYER_NAME':'Collectivités_dépendantes_dun_satellite_admissibles',
'DATASOURCE_OPTIONS':'',
'LAYER_OPTIONS':''})
processing.run("native:savefeatures",
{'INPUT': refetc_fr['OUTPUT'],
'OUTPUT':inputSAVEPATH+'/FR/esri/Collectivités_admissibles_sans_transport_à_grande_capacité_'+version+'.shp',
'LAYER_NAME':'Collectivités_admissibles_sans_transport_à_grande_capacité',
'DATASOURCE_OPTIONS':'',
'LAYER_OPTIONS':''})
processing.run("native:savefeatures",
{'INPUT': refroads_fr['OUTPUT'],
'OUTPUT':inputSAVEPATH+'/FR/esri/Routes_admissibles_'+version+'.shp',
'LAYER_NAME':'Routes_admissibles',
'DATASOURCE_OPTIONS':'',
'LAYER_OPTIONS':''})
#return {'REFSDC': refsdc['OUTPUT'], 'REFETC': refetc['OUTPUT'], 'REFROADS': refroads['OUTPUT']}
return {}
class Renamer (QgsProcessingLayerPostProcessorInterface):
def __init__(self, layer_name):
self.name = layer_name
super().__init__()
def postProcessLayer(self, layer, context, feedback):
layer.setName(self.name)
Related
Merge value of map and insert it into another map
I have a Map <String,List> map1 that looks like that {First: ['1', '2', '3', '4'], Second: ['A', 'B']} I want to create another Map<String,Map<String,int>> as a ruslt of values from map1 to be like that {'1A' : ['String1':10,'String2':20], '1B' : ['String1':10,'String2':20] , '2A' : ['String1':10,'String2':20], '2B' : ['String1':10,'String2':20], '3A' : ['String1':10,'String2':20] , '3C' : ['String1':10,'String2':20]} I hope you get my point
Similar question Generate all combinations from multiple lists Reference Source: https://stackoverflow.com/a/17193002/6576315 void main() async{ Map<String,List> rawMapList = {"First": ['1', '2', '3', '4'], "Second": ['A', 'B']}; List<Map<String, int>> mapResult = [{"String1" : 10}, {"String2" : 20}]; List<String> keyList = <String>[]; generatePermutations(rawMapList.values.toList(), keyList, 0, ""); var result = Map.fromEntries(keyList.map((value) => MapEntry(value, mapResult))); print(result); } void generatePermutations(List<List<dynamic>> lists, List<String> result, int depth, String current) { if (depth == lists.length) { result.add(current); return; } for (int i = 0; i < lists.elementAt(depth).length; i++) { generatePermutations(lists, result, depth + 1, current + lists.elementAt(depth).elementAt(i)); } } Try first on DartPad, This code block will print {1A: [{String1: 10}, {String2: 20}], 1B: [{String1: 10}, {String2: 20}], 2A: [{String1: 10}, {String2: 20}], 2B: [{String1: 10}, {String2: 20}], 3A: [{String1: 10}, {String2: 20}], 3B: [{String1: 10}, {String2: 20}], 4A: [{String1: 10}, {String2: 20}], 4B: [{String1: 10}, {String2: 20}]} Do upvote reference
the solution Map<String, dynamic> data = { 'First': ['1', '2', '3', '4'], 'Second': ['A', 'B'] }; Map<String, dynamic> ans = {}; calculate() { for (var i in (data['First'] as List<dynamic>)) { for (var j in (data['Second'] as List<dynamic>)) { ans.addAll({ "$i$j": [ {'String1': 10}, {'String2': 20} ], }); } } log("$ans"); }
How to properly sort list by another list in Dart
I have two lists, 1 is a list of Map items, and another list which is the order. I would like to sort the items based on their description attribute and compare them with the order list and have them inserted at the top. import 'package:collection/collection.dart'; void main() { List<String> order = [ 'top european', 'top usa', 'top rest of the world' ]; List<Map> items = [ {'id': 0, 'id2': 5, 'description': 'Top USA'}, {'id': 2, 'id2': 2, 'description': 'Top A'}, {'id': 3, 'id2': 0, 'description': 'Top Z'}, {'id': 6, 'id2': 6, 'description': 'Top Rest of the world'}, {'id': 4, 'id2': 4, 'description': 'Top C'}, {'id': 5, 'id2': 1, 'description': 'Top D'}, {'id': 1, 'id2': 3, 'description': 'Top European'}, ]; //this works but adds the items at the end items.sort((a,b) { return order.indexOf(a['description'].toLowerCase()) - order.indexOf(b['description'].toLowerCase()); }); ///Results: print(items); // List<Map> items = [ // {'id': 2, 'id2': 2, 'description': 'Top A'}, // {'id': 3, 'id2': 0, 'description': 'Top Z'}, // {'id': 4, 'id2': 4, 'description': 'Top C'}, // {'id': 5, 'id2': 1, 'description': 'Top D'}, // {'id': 1, 'id2': 3, 'description': 'Top European'}, // {'id': 0, 'id2': 5, 'description': 'Top USA'}, // {'id': 6, 'id2': 6, 'description': 'Top Rest of the world'}, // ]; } SOLUTION: I also tried this approach which is not ideal, but it works. List <Map> itemsOrder = items .where( (ele) => order.contains(ele['description'].toString().toLowerCase())) .toList(); itemsOrder.sort((a, b) { return order.indexOf(a['description'].toLowerCase()) - order.indexOf(b['description'].toLowerCase()); }); items.removeWhere( (ele) => order.contains(ele['description'].toString().toLowerCase())); itemsOrder = itemsOrder.reversed.toList(); for (int i = 0; i < itemsOrder.length; i++) { items.insert(0, itemsOrder[i]); } ///Results: print(items); // List<Map> items = [ // {'id': 1, 'id2': 3, 'description': 'Top European'}, // {'id': 0, 'id2': 5, 'description': 'Top USA'}, // {'id': 6, 'id2': 6, 'description': 'Top Rest of the world'}, // {'id': 2, 'id2': 2, 'description': 'Top A'}, // {'id': 3, 'id2': 0, 'description': 'Top Z'}, // {'id': 4, 'id2': 4, 'description': 'Top C'}, // {'id': 5, 'id2': 1, 'description': 'Top D'}, // ]; Ideally, I would like to use sortBy or sortByCompare but unfortunately, I cannot find a proper example or get a grasp of how to use it.
The way I would fix this is to find the index of the description in the order list and if it cannot be found, I would use a number that is out of index inside the order list to indicate that this item should be at the bottom of the list. This would be my solution: void testIt() { final outOfBounds = order.length + 1; const description = 'description'; items.sort( (lhs, rhs) { final lhsDesc = (lhs[description] as String).toLowerCase(); final rhsDesc = (rhs[description] as String).toLowerCase(); final lhsIndex = order.contains(lhsDesc) ? order.indexOf(lhsDesc) : outOfBounds; final rhsIndex = order.contains(rhsDesc) ? order.indexOf(rhsDesc) : outOfBounds; return lhsIndex.compareTo(rhsIndex); }, ); } And the result is: [{id: 1, id2: 3, description: Top European}, {id: 0, id2: 5, description: Top USA}, {id: 6, id2: 6, description: Top Rest of the world}, {id: 2, id2: 2, description: Top A}, {id: 3, id2: 0, description: Top Z}, {id: 4, id2: 4, description: Top C}, {id: 5, id2: 1, description: Top D}]
Flutter/Dart - Trying to iterate over a map of values // Syntax help needed
I'm a self-learning beginner at coding, failing at a self-set task. The aim of the task is ultimately to figure out the syntax...but I spent hours now. So...help pls! Warning: I thoroughly confused myself...so don't expect beauty in my attempt. I want to use iterate over this map: const marginsCard = [ { 'ID': 0, 'margins': [ {'marginLeft': 0}, {'marginRight': 5}, {'marginBottom': 0}, {'marginTop': 20}, ], }, { 'ID': 1, 'margins': [ {'marginLeft': 5}, {'marginRight': 0}, {'marginBottom': 0}, {'marginTop': 20,}, ], }, { 'ID': 2, 'margins': [ {'marginLeft': 5}, {'marginRight': 0}, {'marginBottom': 0}, {'marginTop': 20,}, ], }, ]; The goals are to iterate over 'ID' hand over the values for the margins to a constructor method that hand back a Card with the specified margins put those Cards in a list. The function calling the constructor function in its current state: buildCardElementList(){ cardElementsList = [ ...(marginsCard[int.parse('ID')] as List<Map<String,Object>>).map((cardElement){ return buildCardElement(cardElement['marginLeft'], cardElement['marginRight'], cardElement['marginBottom'], cardElement['marginTop']); }).toList()]; return cardElementsList; } There is so much try and eror in this, I'm sure there are multiple issues. Can someone help me out with clean syntax so I can start to understand what I'm doing again? Thanks! EDIT // P.S.: I'm leaving out the receiving/constructing function; it's not the issue.
Enjoy ;) void main() { final cards = marginsCard.map(_buildCard).toList(); print(cards); } Card _buildCard(Map<String, Object> cardElement) { return Card( marginLeft: resolveMargin(cardElement, 'marginLeft'), marginRight: resolveMargin(cardElement, 'marginRight'), marginBottom: resolveMargin(cardElement, 'marginBottom'), marginTop: resolveMargin(cardElement, 'marginTop'), ); } num resolveMargin(Map<String, Object> cardElement, String marginName) { final marginElements = cardElement['margins'] as List<Map<String, Object>>; return marginElements.firstWhere((marginElement) => marginElement.containsKey(marginName))[marginName] as num; } class Card { final num marginLeft, marginRight, marginBottom, marginTop; Card({required this.marginLeft, required this.marginRight, required this.marginBottom, required this.marginTop}); #override String toString() => "marginLeft: $marginLeft; marginRight: $marginRight; marginBottom: $marginBottom; marginTop: $marginTop"; } const marginsCard = [ { 'ID': 0, 'margins': [ {'marginLeft': 0}, {'marginRight': 5}, {'marginBottom': 0}, {'marginTop': 20}, ], }, { 'ID': 1, 'margins': [ {'marginLeft': 5}, {'marginRight': 0}, {'marginBottom': 0}, {'marginTop': 20,}, ], }, { 'ID': 2, 'margins': [ {'marginLeft': 5}, {'marginRight': 0}, {'marginBottom': 0}, {'marginTop': 20,}, ], }, ];
Generate list of values, based on matching keys
I have the below dictionary: {'Closed': {'High': 33, 'Medium': 474, 'Low': 47, 'Critical': 6}, 'Impact Statement Pending': {'Low': 3, 'Medium': 1, 'Critical': 0, 'High': 0}, 'New': {'Low': 1, 'High': 2, 'Critical': 2, 'Medium': 2}, 'Remediation Plan Pending': {'Medium': 10, 'Low': 1, 'Critical': 1, 'High': 0}, 'Remedy in Progress': {'Medium': 36, 'Low': 18, 'High': 4, 'Critical': 1}} How might I accomplish creating a list comprised of all values for a specified key? A list for all high values, or another list for all medium values? The way I am currently accomplishing this doesn't seem like the best way. I've got a list of all severity levels, which I iterate over and compare such as shown below: trace_list = ['High', 'Medium', 'Critical', 'Low'] total_status_dict = {'Closed': {'High': 33, 'Medium': 474, 'Low': 47, 'Critical': 6}, 'Impact Statement Pending': {'Low': 3, 'Medium': 1, 'Critical': 0, 'High': 0}, 'New': {'Low': 1, 'High': 2, 'Critical': 2, 'Medium': 2}, 'Remediation Plan Pending': {'Medium': 10, 'Low': 1, 'Critical': 1, 'High': 0}, 'Remedy in Progress': {'Medium': 36, 'Low': 18, 'High': 4, 'Critical': 1}} for item in trace_labels: y_values = [] for key, val in total_status_dict.items(): for ke in total_status_dict[key]: if item is ke: y_values.append(total_status_dict[key][ke])
Note: you are iterating over total_status_dict keys and appending results to a list. Remember that even if dictionaries are officially ordered in Python since 3.7 (see https://docs.python.org/3/whatsnew/3.7.html) you do not always control the Python version of the user. I would rather build a dict key -> item -> value, where key is Closed, Impact Statement Pending, ... and item is one of the trace_labels than a dict key -> [values] where values is supposed to be ordered as in trace_labels. Your code is not efficient because you iterate over trace_labels twice: for item in trace_labels: for ke intotal_status_dict[key]: if item is ke:` How to iterate only once? Instead of building y_values lists one by one (with a whole iteration over total_status_dict each time), you can build several lists at once: >>> trace_labels = ['High', 'Medium', 'Critical', 'Low'] >>> total_status_dict = {'Closed': {'High': 33, 'Medium': 474, 'Low': 47, 'Critical': 6}, 'Impact Statement Pending': {'Low': 3, 'Medium': 1, 'Critical': 0, 'High': 0}, 'New': {'Low': 1, 'High': 2, 'Critical': 2, 'Medium': 2}, 'Remediation Plan Pending': {'Medium': 10, 'Low': 1, 'Critical': 1, 'High': 0}, 'Remedy in Progress': {'Medium': 36, 'Low': 18, 'High': 4, 'Critical': 1}} >>> y_values_by_label = {} >>> for key, value_by_label in total_status_dict.items(): ... for label, value in value_by_label.items(): # total_status_dict[key] is value_by_label ... y_values_by_label.setdefault(label, {})[key] = value ... >>> y_values_by_label {'High': {'Closed': 33, 'Impact Statement Pending': 0, 'New': 2, 'Remediation Plan Pending': 0, 'Remedy in Progress': 4}, 'Medium': {'Closed': 474, 'Impact Statement Pending': 1, 'New': 2, 'Remediation Plan Pending': 10, 'Remedy in Progress': 36}, 'Low': {'Closed': 47, 'Impact Statement Pending': 3, 'New': 1, 'Remediation Plan Pending': 1, 'Remedy in Progress': 18}, 'Critical': {'Closed': 6, 'Impact Statement Pending': 0, 'New': 2, 'Remediation Plan Pending': 1, 'Remedy in Progress': 1}} setdefault(label, {}) creates a empty dict y_values_by_label[label] = {} if y_values_by_label does not have the key label. If you want to turn this in a dict comprehension, you have to use your inefficient method: >>> {label:{k:v for k, value_by_label in total_status_dict.items() for l, v in value_by_label.items() if l==label} for label in trace_labels} {'High': {'Closed': 33, 'Impact Statement Pending': 0, 'New': 2, 'Remediation Plan Pending': 0, 'Remedy in Progress': 4}, 'Medium': {'Closed': 474, 'Impact Statement Pending': 1, 'New': 2, 'Remediation Plan Pending': 10, 'Remedy in Progress': 36}, 'Critical': {'Closed': 6, 'Impact Statement Pending': 0, 'New': 2, 'Remediation Plan Pending': 1, 'Remedy in Progress': 1}, 'Low': {'Closed': 47, 'Impact Statement Pending': 3, 'New': 1, 'Remediation Plan Pending': 1, 'Remedy in Progress': 18}}
How does $max work over an array of objects?
Take an example collection with these documents: client.test.foo.insert_one({ 'name': 'clientA', 'locations': [ {'name': 'a', 'sales': 0, 'leads': 2}, {'name': 'b', 'sales': 5, 'leads': 1}, {'name': 'c', 'sales': 3.3, 'leads': 1}]}) client.test.foo.insert_one({ 'name': 'clientB', 'locations': [ {'name': 'a', 'sales': 6, 'leads': 1}, {'name': 'b', 'sales': 6, 'leads': 3}, {'name': 'c', 'sales': 1.3, 'leads': 4}]}) How does $max determine which item in the location array is maximal? client.test.foo.aggregate([{'$project': {'maxItem': {'$max': '$locations'}}}])) Returns: [{'_id': ObjectId('5b995d72eabb0f0d86dceda5'), 'maxItem': {'leads': 1, 'name': 'b', 'sales': 5}}, {'_id': ObjectId('5b995d72eabb0f0d86dceda6'), 'maxItem': {'leads': 3, 'name': 'b', 'sales': 6}}] It looks like $max is picking to sort on sales but I am not sure why?
I discovered this https://docs.mongodb.com/manual/reference/bson-type-comparison-order/#objects which states: MongoDB’s comparison of BSON objects uses the following order: Recursively compare key-value pairs in the order that they appear within the BSON object. Compare the key field names. If the key field names are equal, compare the field values. If the field values are equal, compare the next key/value pair (return to step 1). An object without further pairs is less than an object with further pairs. which means that if sales is the first key in the bson object then I have my answer. I'm using pymongo and python dictionaries aren't ordered, so I switched to bson.son.SON and re-did the example: client.test.foo.delete_many({}) client.test.foo.insert_one({ 'name': 'clientA', 'locations': [ bson.son.SON([('name', 'a'), ('sales', 0), ('leads', 2)]), bson.son.SON([('name', 'b'), ('sales', 5), ('leads', 1)]), bson.son.SON([('name', 'c'), ('sales', 3.3), ('leads', 1)])]}) client.test.foo.insert_one({ 'name': 'clientB', 'locations': [ bson.son.SON([('name', 'a'), ('sales', 6), ('leads', 1)]), bson.son.SON([('name', 'b'), ('sales', 6), ('leads', 3)]), bson.son.SON([('name', 'c'), ('sales', 1.3), ('leads', 4)])]}) And now its sorting by name: client.test.foo.aggregate([{'$project': {'maxItem': {'$max': '$locations'}}}])) Returns: [{'_id': ObjectId('5b99619beabb0f0d86dcedaf'), 'maxItem': {'leads': 1, 'name': 'c', 'sales': 3.3}}, {'_id': ObjectId('5b99619beabb0f0d86dcedb0'), 'maxItem': {'leads': 4, 'name': 'c', 'sales': 1.3}}]