Ramda.js group by key with unique values - ramda.js

I have the following ramda code and The values are not unique
const transform = pipe(
groupBy (prop ('category')),
map (map (prop('type'))) //uniq is not working here
)
const data = [
{"category": "a", "type" : "a_1"},
{"category": "a", "type" : "a_1"},
{"category": "a", "type" : "a_2"},
{"category": "b", "type" : "b_1"},
{"category": "b", "type" : "b_1"},
]
const result = transform(data)
console.log(result)
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js" integrity="sha512-t0vPcE8ynwIFovsylwUuLPIbdhDj6fav2prN9fEu/VYBupsmrmk9x43Hvnt+Mgn2h5YPSJOk7PMo9zIeGedD1A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>const { pipe, groupBy, prop, map } = R</script>
The result is: {"a": ["a_1", "a_1", "a_2"], "b": ["b_1", "b_1"]}
The expected : {"a": ["a_1", "a_2"], "b": ["b_1"]}
Tanks

After mapping the type, pipe the items into R.uniq:
const { pipe, groupBy, prop, map, uniq } = R
const transform = pipe(
groupBy(prop('category')),
map(pipe(map(prop('type')), uniq))
)
const data = [{"category":"a","type":"a_1"},{"category":"a","type":"a_1"},{"category":"a","type":"a_2"},{"category":"b","type":"b_1"},{"category":"b","type":"b_1"}]
const result = transform(data)
console.log(result)
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js" integrity="sha512-t0vPcE8ynwIFovsylwUuLPIbdhDj6fav2prN9fEu/VYBupsmrmk9x43Hvnt+Mgn2h5YPSJOk7PMo9zIeGedD1A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
You can also replace map(prop('type')) with R.pluck:
const { pipe, groupBy, prop, map, pluck, uniq } = R
const transform = pipe(
groupBy(prop('category')),
map(pipe(pluck('type'), uniq))
)
const data = [{"category":"a","type":"a_1"},{"category":"a","type":"a_1"},{"category":"a","type":"a_2"},{"category":"b","type":"b_1"},{"category":"b","type":"b_1"}]
const result = transform(data)
console.log(result)
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js" integrity="sha512-t0vPcE8ynwIFovsylwUuLPIbdhDj6fav2prN9fEu/VYBupsmrmk9x43Hvnt+Mgn2h5YPSJOk7PMo9zIeGedD1A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

Related

Find min and max values of a field (column) in a feature layer on ArcGIS in a Nuxt/Vuejs application

I am using ArcGIS API for Javascript in a Nuxt application and I would like to find the max and min values of a field in a feature layer hosted on ArcGIS. How to do that ?? Here is the code. The values for the stops for the visualVariables are hard coded and I would like them to be dynamic and take the min and max values of the field.
buildRenderer(fieldName) {
this.renderer = {
type: 'simple', // autocasts as new SimpleRenderer()
symbol: {
type: 'simple-line', // autocasts as new SimpleFillSymbol()
width: '1px',
},
label: 'GIMM',
visualVariables: [
{
type: 'color',
valueExpression: `$feature.${fieldName}`,
legendOptions: {
title: '% KBA stuff',
},
stops: [
{
value: 10,
color: '#bef264',
label: 'minimum',
},
{
value: 600000,
color: '#881337',
label: 'maximum',
},
],
},
],
}
You can make an statistic query requesting min and max of the field you need.
Look at this example I put for you,
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<title>
Intro to FeatureLayer | Sample | ArcGIS API for JavaScript 4.23
</title>
<link rel="stylesheet" href="https://js.arcgis.com/4.23/esri/themes/light/main.css" />
<script src="https://js.arcgis.com/4.23/"></script>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
<script>
require([
"esri/Map",
"esri/views/MapView",
"esri/layers/FeatureLayer",
"esri/rest/support/Query"
], (
Map,
MapView,
FeatureLayer,
Query
) => {
const featureLayer = new FeatureLayer({
url: "https://services.arcgis.com/V6ZHFr6zdgNZuVG0/ArcGIS/rest/services/subway_tcl_stations_center_wgs84/FeatureServer/0"
});
const minXCoord = {
onStatisticField: "XCoord",
outStatisticFieldName: "minXCoord",
statisticType: "min"
};
const maxXCoord = {
onStatisticField: "XCoord",
outStatisticFieldName: "maxXCoord",
statisticType: "max"
};
let query = featureLayer.createQuery();
query.outStatistics = [ minXCoord, maxXCoord ];
featureLayer.queryFeatures(query)
.then(function (response) {
const stats = response.features[0].attributes;
console.log(stats);
document.getElementById("response").innerText =
`X Coord (min, max): ${stats.minXCoord}, ${stats.maxXCoord}`;
});
});
</script>
</head>
<body>
<div id="response"></div>
</body>
</html>

ArcGIS map shows all continents (zoom out completely) when using wkid 102704

I am referring an example provided in, https://developers.arcgis.com/documentation/core-concepts/features-and-geometries/#polygons for drawing a polygon and move the map to that location. The example is working fine. But when I use my custom details such as the rings values and WKID, the polygon is drawing in the location but the map appears completely zoom out such that all the continents are appearing (Please check the image attached). It is required to zoom to the location by click on the '+' widget. Please find the code below.
enter image description here
I have commented the example wkid and ring values.
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="https://js.arcgis.com/4.16/esri/themes/light/main.css" />
<script src="https://js.arcgis.com/4.16/"></script>
<style>
html,
body,
#mapDiv,
.map .container {
padding: 0;
margin: 0;
height: 100%;
width: 100vw !important;
}
</style>
</head>
<body>
<script>
require([
"esri/Map",
"esri/views/MapView",
"esri/layers/FeatureLayer",
"esri/geometry/Polygon",
"esri/Graphic",
"esri/symbols/SimpleFillSymbol",
"esri/geometry/support/webMercatorUtils",
"dojo/domReady!"
], function(
Map,
MapView,
FeatureLayer,
Polygon,
Graphic,
SimpleFillSymbol,
webMercatorUtils
) {
var map = new Map({
basemap: "streets-navigation-vector"
});
// 102704 - Custom WKID
// 4326 - Example WKID
var poly = new Polygon({
spatialReference: {
wkid: 102704
},
rings: [
// [
// [-118.38516, 34.0127],
// [-118.38827, 34.01489],
// [-118.38813, 34.01602],
// [-118.38797, 34.01648],
// [-118.3876, 34.01712],
// [-118.38733, 34.01696],
// [-118.38696, 34.01749],
// [-118.38662, 34.01789],
// [-118.38689, 34.01805],
// [-118.38683, 34.01812],
// [-118.38295, 34.01592],
// [-118.38516, 34.0127]
// ],
// [
// [-118.38661, 34.01486],
// [-118.38634, 34.01498],
// [-118.38652, 34.01563],
// [-118.3867, 34.01559],
// [-118.38679, 34.01595],
// [-118.38699, 34.01591],
// [-118.38707, 34.01507],
// [-118.38661, 34.01486]
// ]
[
[
2744913.4668447226,
541568.06113781035
],
[
2744917.4038447142,
541499.65215389431
],
[
2744864.2454864681,
541496.82210706174
],
[
2744813.6648789644,
541494.12952713668
],
[
2744810.2104895562,
541563.64283956587
],
[
2744860.4905727208,
541565.79441006482
],
[
2744913.4668447226,
541568.06113781035
]
]
]
});
var view = new MapView({
container: "mapDiv",
map: map,
// zoom: 18,
// minZoom: 13,
basemap: "satellite",
// extent: webMercatorUtils.geographicToWebMercator(poly.extent.expand(3))
});
var symbol = new SimpleFillSymbol({
style: "solid",
color: [4, 121, 193, 0.5],
outline: {
width: 2,
color: [2, 94, 149, 1]
}
});
var graphic = new Graphic({
geometry: poly,
symbol: symbol
});
view.on("mouse-wheel", function(event) {
event.stopPropagation();
var doc = document.documentElement;
var top = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
window.scroll(0, top + event.deltaY);
});
view.graphics.add(graphic);
});
</script>
<div style="" id="mapDiv"></div>
<mat-button-toggle-group [multiple]="true" name="fontStyle" aria-label="Font Style">
<button id="parcels" value="bold">Parcels</button>
<button id="housenumbers" value="italic">House Numbers</button>
<button id="said" value="underline">Sample Area IDs</button>
</mat-button-toggle-group>
</body>
</html>
The problem is that you need to reproject your custom coordinates (assume we call custom to other coordinate rather than WebMercator 3857 or 102100 and Geographics 4326). In order to do so,
you can use the GeometryService to interact with a geometry service of an ArcGIS Server,
or you could try pojection module to do it in the client.
Here you have a working example base on your code, that uses GeometryService.
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="https://js.arcgis.com/4.16/esri/themes/light/main.css" />
<script src="https://js.arcgis.com/4.16/"></script>
<style>
html,
body,
#mapDiv,
.map .container {
padding: 0;
margin: 0;
height: 100%;
width: 100vw !important;
}
</style>
</head>
<body>
<script>
require([
"esri/Map",
"esri/views/MapView",
"esri/geometry/Polygon",
"esri/geometry/SpatialReference",
"esri/Graphic",
"esri/tasks/GeometryService",
"esri/tasks/support/ProjectParameters",
"dojo/domReady!"
], function (
Map,
MapView,
Polygon,
SpatialReference,
Graphic,
GeometryService,
ProjectParameters
) {
const map = new Map({
basemap: "streets"
});
const view = new MapView({
container: "mapDiv",
map: map
});
// 102704 - Custom WKID
const originalPoly = new Polygon({
spatialReference: {
wkid: 102704
},
rings: [
[
[
2744913.4668447226,
541568.06113781035
],
[
2744917.4038447142,
541499.65215389431
],
[
2744864.2454864681,
541496.82210706174
],
[
2744813.6648789644,
541494.12952713668
],
[
2744810.2104895562,
541563.64283956587
],
[
2744860.4905727208,
541565.79441006482
],
[
2744913.4668447226,
541568.06113781035
]
]
]
});
console.log(`Original Polygon: ${JSON.stringify(originalPoly.toJSON())}`);
const geomSer = new GeometryService(
"http://sampleserver6.arcgisonline.com/arcgis/rest/services/Utilities/Geometry/GeometryServer"
);
const outSpatialReference = new SpatialReference({ wkid: 102100 });
const params = new ProjectParameters({
geometries: [originalPoly],
outSpatialReference
});
geomSer.project(params).then(function(result) {
const projectedPoly = result[0];
if (!projectedPoly) {
return;
}
console.log(`Projected Polygon: ${JSON.stringify(projectedPoly.toJSON())}`);
view.graphics.add(new Graphic({
geometry: projectedPoly,
symbol: {
type: "simple-fill",
style: "solid",
color: [255, 0, 0, 0.1],
outline: {
width: 2,
color: [255, 0, 0, 1]
}
}
}));
view.extent = projectedPoly.extent.clone().expand(3);
});
});
</script>
<div id="mapDiv"></div>
</body>
</html>
ArcGIS API - GeometryService
ArcGIS API - projection

Detect if a user has created a 4x4 line in a 4x4 grid of divs

So I am building my own custom bingo table that is 4x4 with vue. I have everything down except how to detect wether a user has formed a 4x4 line that is horizontal, diagonal, or vertical
Inside my data function I have an array that represents the 4x4
[9, 13, 28, 24],
[11, 22, 15, 43],
[54, 5, 37, 4],
[27, 40, 12, 36]
My question is how can I check to see if a user has clicked a 4x4 line? No code is needed as an answer I just want to know how I can approach this problem.
Given an n x n matrix
Horizontal:
Are there n selected elements that have the same rowIndex?
Vertical:
Are there n selected elements that have the same columnIndex?
Diagonal (Top Left to Bottom Right)
Are there n elements that have the same rowIndex as their columnIndex?
Diagonal (Top Right to Bottom Left)
Are there n elements that satisfy (length(row) - 1) - rowIndex == columnIndex?
const Card = Vue.component('card', {
template: '#card',
props: {
playerCard: Array
},
data() {
return {
selectedVals: [],
rowCounts: {},
colCounts: {}
}
},
computed: {
horizontalNumberToWin() {
return this.playerCard[0].length;
},
verticalNumberToWin() {
return this.playerCard.length;
},
diagonalNumberToWin() {
return this.playerCard.length;
},
isDiagonal() {
if (this.selectedVals.length < this.diagonalNumberToWin) return false;
// top left to bottom right
// [0, 0] [1, 1], [2, 2], [3, 3], etc..
const isTLtoBR = this.selectedVals.filter(val => val[0] === val[1]);
if (isTLtoBR.length >= this.diagonalNumberToWin) return true;
// top right to bottom left
// [0, 3], [1, 2], [2, 1], [3, 0], etc..
const rowLen = this.playerCard[0].length;
const isTRtoBL = this.selectedVals.filter(val => {
return (rowLen -1) - val[0] === val[1];
});
if (isTRtoBL.length >= this.diagonalNumberToWin) return true;
return false;
},
isHorizontal() {
if (this.selectedVals.length < this.horizontalNumberToWin) return false;
return Object.values(this.rowCounts).some(row => row >= this.horizontalNumberToWin);
},
isVertical() {
if (this.selectedVals.length < this.verticalNumberToWin) return false;
return Object.values(this.colCounts).some(col => col >= this.verticalNumberToWin);
},
},
methods: {
onCardClicked(coord) {
this.selectedVals.push(coord);
this.updateCounts(coord);
},
cardDisabled(coord) {
return this.selectedVals.some(vals => vals[0] === coord[0] && vals[1] === coord[1]);
},
updateCounts(coord) {
const rowIndex = coord[0];
const colIndex = coord[1];
this.rowCounts[rowIndex] = this.rowCounts[rowIndex] ? this.rowCounts[rowIndex] + 1 : 1;
this.colCounts[colIndex] = this.colCounts[colIndex] ? this.colCounts[colIndex] + 1 : 1;
}
}
});
new Vue({
el: '#app',
components: {
Card,
},
data: {
playerCard: [
[9, 13, 28, 24],
[11, 22, 15, 43],
[54, 5, 37, 4],
[27, 40, 12, 36]
],
},
})
#app {
display: flex;
flex-direction: row;
justify-content: center;
}
.board {
max-width: 500px;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr 1fr;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
<card :player-card="playerCard" />
</div>
<template id="card">
<div>
<p>Horizontal: {{ isHorizontal }}</p>
<p>Vertical: {{ isVertical }}</p>
<p>Diagonal: {{ isDiagonal }}</p>
<div class="board">
<template v-for="(row, rowIndex) in playerCard">
<button
v-for="(col, colIndex) in row"
:key="col"
:disabled="cardDisabled([rowIndex, colIndex])"
#click="onCardClicked([rowIndex, colIndex])">
{{ col }}
</button>
</template>
</div>
</div>
</template>
First, I think you should add isClick to check whether the user has clicked the card or not.
[
[
{ Number: 9 , isClick: false },
{ Number: 13 , isClick: false },
{ Number: 28 , isClick: false },
{ Number: 24 , isClick: false }
],
// other array ...
]
Second, bind your data into html (Maybe you should used twice v-for to achieve it). When user click the card, set isClick = true.
Third, write your own logic to check if a user has clicked a 4x4 line.

None of the chartist plugins are working

I have a working chartist Line chart and I have configured the plugins as suggested in documentations. I don't get any errors when loading the page. Its just that nothing gets reflected on the chart according to plugin. I have added two plugins - they don't show any error and my line chart shows perfectly fine.
But I see no effect of those plugins - tooltip plugin and pointlabel plugin.
And yes they are loaded in the HTML and their css files are also included else would have got errors about plugins not being present.
var options = {
low: 0,
high: 100,
showGridBackground: false,
showArea: true,
axisX: {
showGrid: false
},
axisY: {
},
plugins: [
Chartist.plugins.ctPointLabels({
textAnchor: 'middle',
labelInterpolationFnc: function(value) {console.log("i was called"); return '$' + value}
}),
Chartist.plugins.tooltip({
})
]
};
var m = new Chartist.Line('#myChart', data, options);
Here is simple working example using your code. One thing to watch out for is that the tooltips need additional CSS to display correctly.
https://jsfiddle.net/rxdb576n/1/
var data = {
labels: ["Mon", "Tue", "Wed"],
series: [
[10, 20, 75]
],
}
var options = {
low: 0,
high: 100,
showGridBackground: false,
showArea: true,
axisX: {
showGrid: false
},
axisY: {},
plugins: [
Chartist.plugins.ctPointLabels({
textAnchor: 'middle',
labelInterpolationFnc: function(value) {
console.log("i was called");
return '$' + value
}
}),
Chartist.plugins.tooltip({
})
]
};
var m = new Chartist.Line('#myChart', data, options);
.chartist-tooltip {
opacity: 0;
position: absolute;
margin: 20px 0 0 10px;
background: rgba(0, 0, 0, 0.8);
color: #FFF;
padding: 5px 10px;
border-radius: 4px;
}
.chartist-tooltip.tooltip-show {
opacity: 1;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/chartist/0.11.0/chartist.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartist/0.11.0/chartist.min.js"></script>
<script src="https://unpkg.com/chartist-plugin-tooltips#0.0.17"></script>
<script src="https://unpkg.com/chartist-plugin-pointlabels#0.0.6"></script>
<div id="myChart"></div>

cytoscape js event when all elements rendered?

Is there an event emitted when cy.add(elements) is finished? It appears there's an event fired for each element added, but I don't see an event when all elements have been added and rendered.
ex:
var elements = [ { data: {id: 'n1'} }, { data: {id: 'n2'} }, { data: {id: 'n3'} }, ];
cy.add(elements);
cy.on('add',function(evt){
console.log('Element Added')
})
The log will run three times.
Update I refactored per your comment. This will allow you to detect and do something after each "batch" is added.
I started with an example from the cytoscape.js website and heavily edited it for this answer.
You will probably want to run the snippet full screen to see the canvas and the console at the same time.
// For generating new IDs
var ids = [];
function newId() {
if (!ids.length) {
ids.push('1');
} else {
var id = '' + (parseInt(ids[ids.length - 1]) + 1);
ids.push(id);
}
return ids[ids.length - 1];
}
// Draw for first time
var cy = cytoscape({
container: document.getElementById('cy'),
elements: [], // don't add elements initially
style: [{
selector: 'node',
style: {
'background-color': '#666',
'label': 'data(id)',
'width': 10,
'height': 10
}
}, {
selector: 'edge',
style: {
'width': 3,
'line-color': '#ccc',
'target-arrow-color': '#ccc',
'target-arrow-shape': 'triangle'
}
}],
layout: {
name: 'grid',
rows: 1
}
});
var batchTotal = 0;
var totalAdded = 0;
// Start listening to events
cy.on('add', function(evt) {
console.log('event heard: add');
if (totalAdded < batchTotal) {
totalAdded++;
}
console.log('totalAdded/batchTotal: ', totalAdded + '/' + batchTotal);
if (totalAdded == batchTotal) {
console.log('entire batch added');
// do whatever you want!
}
});
var xStart = 10;
var x = xStart;
var y = 40;
var xInc = 20;
var yInc = 30;
var max = 200;
var addTotal = 2;
// For adding new elements
function add() {
// let's build multiple elements to add at once
var elements = [];
var id;
var el;
for (var i = 0; i < addTotal; i++) {
id = newId();
el = {
data: {
id: id
},
position: {
x: x,
y: y
}
};
elements.push(el);
if (x <= max) {
x += xInc;
} else {
x = xStart;
y += yInc;
}
}
addElements(elements);
}
// wrap the cy.add() method
// so we can inject our total counter
function addElements (elements) {
if (!Array.isArray(elements)) elements = [elements]; // convert to array
// this would be useful in a more robust app where
// addTotal may change
batchTotal = elements.length;
// reset
totalAdded = 0;
cy.add(elements);
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>CytoscapeJS Example</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/2.7.0/cytoscape.js"></script>
<style type="text/css">
#cy {
width: 300px;
height: 300px;
display: block;
}
</style>
</head>
<body>
<button onclick="add()">Add</button>
<p>Click add and watch the console. Look for the <strong>entire batch added</strong> message.</p>
<div id="cy"></div>
<script type="text/javascript" src="js/app.js"></script>
</body>
</html>