Hi I am working with the next code:
private fun getAttributesMap(navMenu: NavItem?): AttributesMap {
var attributesString = navMenu?.attributes
val attributesMap = mutableMapOf<String, String>()
attributesString?.lines()?.map {
val pair = it.split("=")
if (pair?.size == 2) {
attributesMap.put(pair[0], pair[1])
}
}
return AttributesMap(attributesMap)
}
But according with the documentation: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/map.html
I should be able to assign the result of this transformation to attributesMap. Any clues about how it will be with the kotlin .map approach?
Thanks!!
A combination of map and filter will work:
val attributesMap = attributesString.lines()
.map { it.split("=") }
.filter { it.size == 2 } //filter all with more or less elements
.map { it[0] to it[1] } //convert to Pair
.toMap() //convert to Map
Related
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val mapFragment = childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment?
mapFragment?.getMapAsync(this)
}
#SuppressLint("MissingPermission", "PotentialBehaviorOverride")
override fun onMapReady(googleMap: GoogleMap?) {
map = googleMap!!
map.isMyLocationEnabled = true
map.setOnMyLocationButtonClickListener(this)
map.setOnMarkerClickListener(this)
map.uiSettings.apply {
isZoomControlsEnabled = false
isZoomGesturesEnabled = false
isRotateGesturesEnabled = false
isTiltGesturesEnabled = false
isCompassEnabled = false
isScrollGesturesEnabled = false
}
observeTrackerService()
}
private fun observeTrackerService() {
TrackerService.locationList.observe(viewLifecycleOwner, {
if (it != null) {
locationList = it
if (locationList.size > 1) {
binding.stopButton.enable()
}
drawPolyline()
followPolyline()
}
})
TrackerService.started.observe(viewLifecycleOwner, {
started.value = it
})
TrackerService.startTime.observe(viewLifecycleOwner, {
startTime = it
})
TrackerService.stopTime.observe(viewLifecycleOwner, {
stopTime = it
if (stopTime != 0L) {
showBiggerPicture()
displayResults()
}
})
}
private fun drawPolyline() {
val polyline = map.addPolyline(
PolylineOptions().apply {
width(10f)
color(Color.BLUE)
jointType(JointType.ROUND)
startCap(ButtCap())
endCap(ButtCap())
addAll(locationList)
}
)
polylineList.add(polyline)
}
private fun followPolyline() {
if (locationList.isNotEmpty()) {
map.animateCamera(
(CameraUpdateFactory.newCameraPosition(
setCameraPosition(
locationList.last()
)
)), 1000, null)
}
}
}
private fun showBiggerPicture() {
val bounds = LatLngBounds.Builder()
for (location in locationList) {
bounds.include(location)
}
map.animateCamera(
CameraUpdateFactory.newLatLngBounds(
bounds.build(), 100
), 2000, null
)
addMarker(locationList.first())
addMarker(locationList.last())
}
private fun addMarker(position: LatLng){
val marker = map.addMarker(MarkerOptions().position(position))
markerList.add(marker)
}
private fun displayResults() {
val result = Result(
calculateTheDistance(locationList),
calculateElapsedTime(startTime, stopTime)
)
lifecycleScope.launch {
delay(2500)
val directions = MapsFragmentDirections.actionMapsFragmentToResultFragment(result)
findNavController().navigate(directions)
binding.startButton.apply {
hide()
enable()
}
binding.stopButton.hide()
binding.resetButton.show()
} `
I would like to send the polylines to Firestore. How to send the polylines in code to Firestore? Can anyone help? My code has a map fragment with buttons. This is a distance tracking app. The app plots the distance using polylines. How to convert polylines to arrays. Here I am doing a location tracking app. I want to convert the polylines to arrays so that I could save it cloud.
According to the official documentation, a Polyline is not a Firestore supported data-type. So there is no way you can add such an object to Firestore.
What's a polyline?
It's basically a list of points. So what you can do instead is to add all these points to Firestore. You can add them as simple as latitude and longitude or as GeoPoint objects. If you have additional details for the locations, you can add them as documents in a collection, otherwise, you can store them in an array within a document.
To read them, simply create a reference to the document, loop through the array, create a new LatLng object of each location, and add all of them to the polyline.
I am studying Ceylon and have question about it metamodel. I want to create some create some base class 'DataContainer' which allow to instantiate immutable classes with build-in equals-hash implementation:
e.g. Identifier(125, "ab") == Identifier(125, "ab")
So base class should collect all shared non-variable values and use this information in 'hash' an 'equals' methods.
I have wrote this code:
shared abstract class DataContainer(ClassDeclaration declaration) {
value members = {
for (i in declaration.memberDeclarations<ValueDeclaration>())
if (!i.variable, i.name != "hash", i.name != "string") i
};
variable Integer? hashCode = null;
shared actual Boolean equals(Object that) {
if (is DataContainer that) {
for (item in members) {
value thisMember = item.memberGet(this);
value thatMember = item.memberGet(that);
if (exists thisMember, exists thatMember) {
if (thisMember != thatMember) { return false; }
} else if (thisMember exists != thatMember exists) { return false; }
}
return true;
}
return false;
}
shared actual Integer hash => hashCode else (hashCode = calculateHash());
Integer calculateHash() {
variable value result = 0;
for(i in members) {
if (exists member = i.memberGet(this)) {
result = result.xor(member.hash);
}
}
return result;
}
}
class Identifier(shared Integer? id, shared String? name) extends DataContainer(`class`) {}
The Identifier class is the client of DataContainer. I like this solution in whole but I have to pass 'class' into the super class constructor because if I use 'class' inside DataContainer it doesn't see any members of subclass.
How can I obtain actual list of extended class's members in base class methods?
Something like 'this' doesn't work...
I found solution thanks to guys from Ceylon community. The function classDeclaration(this) from ceylon.language.meta should be used instead of 'class'.
This is the final code:
shared abstract class DataContainer() {
variable Integer? _hash = null;
variable ValueDeclaration[]? _members = null;
shared actual Boolean equals(Object that) {
if (is DataContainer that) {
for (i in members) {
value thisMember = i.memberGet(this);
value thatMember = i.memberGet(that);
if (exists thisMember, exists thatMember) {
if (thisMember != thatMember) { return false; }
} else if (thisMember exists != thatMember exists) { return false; }
}
return true;
}
return false;
}
shared actual Integer hash => _hash else (_hash = calculateHash());
ValueDeclaration[] members => _members else (_members = [
for (i in classDeclaration(this).memberDeclarations<ValueDeclaration>())
if (!i.variable, i.name != "string", i.name != "hash") i
]);
Integer calculateHash() {
variable Integer result = 0;
for (i in members) {
if (exists member = i.memberGet(this)) {
result = result.xor(member.hash);
}
}
return result;
}
}
class Identifier(shared Integer? number, shared String? name) extends DataContainer() {}
Adding to Alexander's formidable answer, I took the liberty to implement a string function as well so you can just print(myObj) and it renders nicely:
shared default actual String string {
value s = StringBuilder();
s.append(type(this).declaration.name).append(" { ");
for (i in members) {
value m = i.memberGet(this);
s.append(i.name).append(":");
s.append(if(exists m) then m.string else "<null>");
}
return s.append(" }").string;
}
I have seen similar questions but I think my scenario is a bit different. Say I define a collection like this:
MyCol = new Meteor.Collection("myCol"
and I want to get a reference to 'MyCol' using the string 'myCol' - I have created the function below which seems to work:
function GetCollectionObject(name) {
for(var key in window) {
var value = window[key];
if (value instanceof Meteor.Collection) {
if (value._name == name) {
return value;
break;
}
}
}
return null;
}
Is this the only/best/most efficient way to do this?
Why don't you store your collections in a dictionary? It's way more efficient.
Dogs = new Meteor.Collection('dogs');
Cats = new Meteor.Collection('cats');
Alpacas = new Meteor.Collection('alpacas');
MyCollections = {
dogs: Dogs,
cats: Cats,
alpacas: Alpacas,
};
...
MyCollections['dogs'].doSomething();
is it possible to use a Kleene Operator (Kleene Star) for the Formatters?
I want to use a phoneFormatter, which puts a minus after the 5th number and afterwards it should be possible to have a variable number of numbers.
E.g.: 0172-555666999, 0160-44552 etc.
That is how I started, but I don't know which character belongs after the last hash (it is not a star, I already tried it ;-) ):
<fx:Declarations>
<mx:PhoneFormatter id="mPhoneFormat"
formatString="####-#"/>
</fx:Declarations>
The default PhoneFormatter expects the input string to have the same number of characters as the format string. They don't support regular expression patterns (like * to match the element zero or more times).
However, it's pretty easy to make your own formatter. To do this, I extended the PhoneFormatter class and overrode its format() method. I copied and pasted the original format() method and made the following modifications:
comment out the code that compared the length of the source string with the length of the format string
compare the length of the formatted string. If the original string is longer, append the remaining chars from the original string to the formatted string.
This probably won't handle all of your use cases, but it should be pretty straightforward to modify this to your needs.
package
{
import mx.formatters.PhoneFormatter;
import mx.formatters.SwitchSymbolFormatter;
public class CustomPhoneNumberFormatter extends PhoneFormatter
{
public function CustomPhoneNumberFormatter()
{
super();
}
override public function format(value:Object):String
{
// Reset any previous errors.
if (error)
error = null;
// --value--
if (!value || String(value).length == 0 || isNaN(Number(value)))
{
error = defaultInvalidValueError;
return "";
}
// --length--
var fStrLen:int = 0;
var letter:String;
var n:int;
var i:int;
n = formatString.length;
for (i = 0; i < n; i++)
{
letter = formatString.charAt(i);
if (letter == "#")
{
fStrLen++;
}
else if (validPatternChars.indexOf(letter) == -1)
{
error = defaultInvalidFormatError;
return "";
}
}
// if (String(value).length != fStrLen)
// {
// error = defaultInvalidValueError;
// return "";
// }
// --format--
var fStr:String = formatString;
if (fStrLen == 7 && areaCode != -1)
{
var aCodeLen:int = 0;
n = areaCodeFormat.length;
for (i = 0; i < n; i++)
{
if (areaCodeFormat.charAt(i) == "#")
aCodeLen++;
}
if (aCodeLen == 3 && String(areaCode).length == 3)
{
fStr = String(areaCodeFormat).concat(fStr);
value = String(areaCode).concat(value);
}
}
var dataFormatter:SwitchSymbolFormatter = new SwitchSymbolFormatter();
var source:String = String(value);
var returnValue:String = dataFormatter.formatValue(fStr, value);
if (source.length > returnValue.length)
{
returnValue = returnValue + source.substr(returnValue.length-1);
}
return returnValue;
}
}
}
I have an array of objects. Each object has a property called name. I want to efficiently remove an object with a particular name from the array. Is this the BEST way?
private function RemoveSpoke(Name:String):void {
var Temp:Array=new Array;
for each (var S:Object in Spokes) {
if (S.Name!=Name) {
Temp.push(S);
}
}
Spokes=Temp;
}
If you are willing to spend some memory on a lookup table this will be pretty fast:
private function remove( data:Array, objectTable:Object, name:String):void {
var index:int = data.indexOf( objectTable[name] );
objectTable[name] = null;
data.splice( index, 1 );
}
The test for this looks like this:
private function test():void{
var lookup:Object = {};
var Spokes:Array = [];
for ( var i:int = 0; i < 1000; i++ )
{
var obj:Object = { name: (Math.random()*0xffffff).toString(16), someOtherProperty:"blah" };
if ( lookup[ obj.name ] == null )
{
lookup[ obj.name ] = obj;
Spokes.push( obj );
}
}
var t:int = getTimer();
for ( var i:int = 0; i < 500; i++ )
{
var test:Object = Spokes[int(Math.random()*Spokes.length)];
remove(Spokes,lookup,test.name)
}
trace( getTimer() - t );
}
myArray.splice(myArray.indexOf(myInstance), 1);
The fastest way will be this:
function remove(array: Array, name: String): void {
var n: int = array.length
while(--n > -1) {
if(name == array[n].name) {
array.splice(n, 1)
return
}
}
}
remove([{name: "hi"}], "hi")
You can also remove the return statement if you want to get rid of all alements that match the given predicate.
I don't have data to back it up but my guess is that array.filter might be the fastest.
In general you should prefer the old for-loop over "for each" and "for each in" and use Vector if your elements are of the same type. If performance is really important you should consider using a linked list.
Check out Grant Skinners slides http://gskinner.com/talks/quick/ and Jackson Dunstan's Blog for more infos about optimization.
If you don't mind using the ArrayCollection, which is a wrapper for the Array class, you could do something like this:
private function RemoveSpoke(Name:String, Spokes:Array):Array{
var ac:ArrayCollection = new ArrayCollection(Spokes);
for (var i:int=0, imax:int=ac.length; i<imax; i++) {
if (Spokes[i].hasOwnProperty("Name") && Spokes[i].Name === Name) {
ac.removeItemAt(i);
return ac.source;
}
}
return ac.source;
}
You could also use ArrayCollection with a filterFunction to get a view into the same Array object
Perhaps this technique (optimized splice method by CJ's) will further improve the one proposed by Quasimondo:
http://cjcat.blogspot.com/2010/05/stardust-v11-with-fast-array-splicing_21.html
Here's an efficient function in terms of reusability, allowing you to do more than remove the element. It returns the index, or -1 if not found.
function searchByProp(arr:Array, prop:String, value:Object): int
{
var item:Object;
var n: int = arr.length;
for(var i:int=n;i>0;i--)
{
item = arr[i-1];
if(item.hasOwnProperty(prop))
if( value == item[prop] )
return i-1;
}
return -1;
}