loop through objects inside an array in a regex - arrays

EDITED
I got an array of objects:
values: [
{ key: "CollinHenderson" },
{ key: "SarahDrasner" },
{ key: "EvanYou" },
{ key: "AdamWathan" }
]
I want to highlight the text if one of them is mentioned ("#..."):
.innerHTML
.replace(
/(?:|^)#[A-Za-z0-9\\-\\.\\__äÄöÖüÜß]+(?:|$)/g,
"<span contenteditable='false' class='markAt'>$&</span>"
);
with this regex (above) everything gets highlighted which starts with a '#'. How could I loop trough my array of objects inside of my regex, so that it only turns out true if one of my users get mentioned. For Example:
"#hello" should be false
"#CollinHenderson" should be true and get therefore highlighted
/(?:|^)#[ //loop through array - if matches a value -> true // ]+(?:|$)/g,

You can pass a function to replace and check in there
const values = [{
key: "CollinHenderson"
},
{
key: "SarahDrasner"
},
{
key: "EvanYou"
},
{
key: "AdamWathan"
}
]
const el = document.getElementById('foo');
el.addEventListener('keyup', e => {
document.getElementById('bar').innerHTML = e.target.value
.replace(
/(?:|^)#[A-Za-z0-9\\-\\.\\__äÄöÖüÜß]+(?:|$)/g,
m => {
if (values.find(v => m.substring(1) === v.key)) return "<span contenteditable='false' class='markAt'>"+ m + "</span>";
return m;
}
);
});
.markAt {
font-weight: 700;
}
<textarea id="foo"></textarea>
<div id="bar">
</div>

Related

Go JS Tree Mapping view in React TS

I want to achieve Tree Mapping from Gojs in React typescript.
I found one example of Go js in react but it's still confusing for me to make Tree Mapping in react. Any help would be appreciated to start with Tree Mapping in React TS.
Here is my repo: https://github.com/AdarshPawar29/map-tree-gojs
I manage to create the tree mapping but still some work to be done. I commented on some of the not working functions. I'm also looking for the functional component.
import React, { useState } from "react";
import * as go from "gojs";
import { ReactDiagram } from "gojs-react";
import "./App.css";
class TreeNode extends go.Node {
constructor() {
super();
this.treeExpandedChanged = (node) => {
if (node.containingGroup !== null) {
node.containingGroup
.findExternalLinksConnected()
.each((l) => l.invalidateRoute());
}
};
}
// findVisibleNode() {
// // redirect links to lowest visible "ancestor" in the tree
// var n = this;
// while (n !== null && !n.isVisible()) {
// n = n.findTreeParentNode();
// }
// return n;
// }
}
// end TreeNode
// Control how Mapping links are routed:
// - "Normal": normal routing with fixed fromEndSegmentLength & toEndSegmentLength
// - "ToGroup": so that the link routes stop at the edge of the group,
// rather than going all the way to the connected nodes
// - "ToNode": so that they go all the way to the connected nodes
// but only bend at the edge of the group
var ROUTINGSTYLE = "Normal";
// If you want the regular routing where the Link.[from/to]EndSegmentLength controls
// the length of the horizontal segment adjacent to the port, don't use this class.
// Replace MappingLink with a go.Link in the "Mapping" link template.
class MappingLink extends go.Link {
getLinkPoint(
node: go.Node | null | any,
port: go.GraphObject,
spot: go.Spot,
from: boolean,
ortho: boolean,
othernode: go.Node | null | any,
otherport: go.GraphObject
) {
if (ROUTINGSTYLE !== "ToGroup") {
return super.getLinkPoint(
node,
port,
spot,
from,
ortho,
othernode,
otherport
);
} else {
var r = port.getDocumentBounds();
var group = node.containingGroup;
var b = group !== null ? group.actualBounds : node.actualBounds;
var op = othernode.getDocumentPoint(go.Spot.Center);
var x = op.x > r.centerX ? b.right : b.left;
return new go.Point(x, r.centerY);
}
}
computePoints() {
var result = super.computePoints();
if (result && ROUTINGSTYLE === "ToNode") {
var fn = this.fromNode;
var tn = this.toNode;
if (fn && tn) {
var fg = fn.containingGroup;
var fb = fg ? fg.actualBounds : fn.actualBounds;
var fpt = this.getPoint(0);
var tg = tn.containingGroup;
var tb = tg ? tg.actualBounds : tn.actualBounds;
var tpt = this.getPoint(this.pointsCount - 1);
this.setPoint(
1,
new go.Point(fpt.x < tpt.x ? fb.right : fb.left, fpt.y)
);
this.setPoint(
this.pointsCount - 2,
new go.Point(fpt.x < tpt.x ? tb.left : tb.right, tpt.y)
);
}
}
return result;
}
}
// end MappingLink
// Create some random trees in each group
var nodeDataArray = [
{ isGroup: true, key: -1, text: "Left Side", xy: "0 0", width: 150 },
{ isGroup: true, key: -2, text: "Right Side", xy: "300 0", width: 150 },
];
var linkDataArray = [
{ from: 6, to: 1012, category: "Mapping" },
{ from: 4, to: 1006, category: "Mapping" },
{ from: 9, to: 1004, category: "Mapping" },
{ from: 1, to: 1009, category: "Mapping" },
{ from: 14, to: 1010, category: "Mapping" },
];
export default function App() {
// All links must go from a node inside the "Left Side" Group to a node inside the "Right Side" Group.
function checkLink(
fn: {
[x: string]: any;
containingGroup: { data: { key: number } } | null;
},
fp: any,
tn: {
[x: string]: any;
containingGroup: { data: { key: number } } | null;
},
tp: any,
link: any
) {
// make sure the nodes are inside different Groups
if (fn.containingGroup === null || fn.containingGroup.data.key !== -1)
return false;
if (tn.containingGroup === null || tn.containingGroup.data.key !== -2)
return false;
//// optional limit to a single mapping link per node
if (
fn.linksConnected.any(
(l: { category: string }) => l.category === "Mapping"
)
)
return false;
if (
tn.linksConnected.any(
(l: { category: string }) => l.category === "Mapping"
)
)
return false;
return true;
}
function initDiagram() {
const $ = go.GraphObject.make;
// set your license key here before creating the diagram: go.Diagram.licenseKey = "...";
const diagram = $(go.Diagram, {
"undoManager.isEnabled": true, // must be set to allow for model change listening
"undoManager.maxHistoryLength": 0, // uncomment disable undo/redo functionality
"clickCreatingTool.archetypeNodeData": {
text: "new node",
color: "lightblue",
},
"commandHandler.copiesTree": true,
"commandHandler.deletesTree": true,
// newly drawn links always map a node in one tree to a node in another tree
"linkingTool.archetypeLinkData": { category: "Mapping" },
"linkingTool.linkValidation": checkLink,
"relinkingTool.linkValidation": checkLink,
model: new go.GraphLinksModel({
linkKeyProperty: "key", // IMPORTANT! must be defined for merges and data sync when using GraphLinksModel
}),
});
// // define a simple Node template
// diagram.nodeTemplate = $(
// go.Node,
// "Auto", // the Shape will go around the TextBlock
// new go.Binding("location", "loc", go.Point.parse).makeTwoWay(
// go.Point.stringify
// ),
// $(
// go.Shape,
// "RoundedRectangle",
// { name: "SHAPE", fill: "white", strokeWidth: 0 },
// // Shape.fill is bound to Node.data.color
// new go.Binding("fill", "color")
// ),
// $(
// go.TextBlock,
// { margin: 8, editable: true }, // some room around the text
// new go.Binding("text").makeTwoWay()
// )
// );
diagram.nodeTemplate = $(
TreeNode,
{ movable: false, copyable: false, deletable: false }, // user cannot move an individual node
// no Adornment: instead change panel background color by binding to Node.isSelected
{
selectionAdorned: false,
background: "white",
mouseEnter: (e, node) => (node.background = "aquamarine"),
// mouseLeave: (e, node) =>
// (node.background = node.isSelected ? "skyblue" : "white"),
},
new go.Binding("background", "isSelected", (s) =>
s ? "skyblue" : "white"
).ofObject(),
// whether the user can start drawing a link from or to this node depends on which group it's in
new go.Binding("fromLinkable", "group", (k) => k === -1),
new go.Binding("toLinkable", "group", (k) => k === -2),
$(
"TreeExpanderButton", // support expanding/collapsing subtrees
{
width: 14,
height: 14,
"ButtonIcon.stroke": "white",
"ButtonIcon.strokeWidth": 2,
"ButtonBorder.fill": "goldenrod",
"ButtonBorder.stroke": null,
"ButtonBorder.figure": "Rectangle",
_buttonFillOver: "darkgoldenrod",
_buttonStrokeOver: null,
_buttonFillPressed: null,
}
),
$(
go.Panel,
"Horizontal",
{ position: new go.Point(16, 0) },
//// optional icon for each tree node
//$(go.Picture,
// { width: 14, height: 14,
// margin: new go.Margin(0, 4, 0, 0),
// imageStretch: go.GraphObject.Uniform,
// source: "images/defaultIcon.png" },
// new go.Binding("source", "src")),
$(go.TextBlock, new go.Binding("text", "key", (s) => "item " + s))
) // end Horizontal Panel
); // end Node
// These are the links connecting tree nodes within each group.
diagram.linkTemplate = $(go.Link); // without lines
diagram.linkTemplate = // with lines
$(
go.Link,
{
selectable: false,
routing: go.Link.Orthogonal,
fromEndSegmentLength: 4,
toEndSegmentLength: 4,
fromSpot: new go.Spot(0.001, 1, 7, 0),
toSpot: go.Spot.Left,
},
$(go.Shape, { stroke: "lightgray" })
);
// These are the blue links connecting a tree node on the left side with one on the right side.
diagram.linkTemplateMap.add(
"Mapping",
$(
MappingLink,
{
isTreeLink: false,
isLayoutPositioned: false,
layerName: "Foreground",
},
{ fromSpot: go.Spot.Right, toSpot: go.Spot.Left },
{ relinkableFrom: true, relinkableTo: true },
$(go.Shape, { stroke: "blue", strokeWidth: 2 })
)
);
function updateNodeWidths(
group: {
memberParts: {
each: (arg0: {
(n: any): void;
(n: any): void;
(n: any): void;
}) => void;
};
},
width: number
) {
if (isNaN(width)) {
group.memberParts.each((n: { width: number }) => {
if (n instanceof go.Node) n.width = NaN; // back to natural width
});
} else {
var minx = Infinity; // figure out minimum group width
group.memberParts.each((n: { actualBounds: { x: number } }) => {
if (n instanceof go.Node) {
minx = Math.min(minx, n.actualBounds.x);
}
});
if (minx === Infinity) return;
var right = minx + width;
group.memberParts.each(
(n: { width: number; actualBounds: { x: number } }) => {
if (n instanceof go.Node)
n.width = Math.max(0, right - n.actualBounds.x);
}
);
}
}
function makeGroupLayout() {
return $(
go.TreeLayout, // taken from samples/treeView.html
{
alignment: go.TreeLayout.AlignmentStart,
angle: 0,
compaction: go.TreeLayout.CompactionNone,
layerSpacing: 16,
layerSpacingParentOverlap: 1,
nodeIndentPastParent: 1.0,
nodeSpacing: 0,
setsPortSpot: false,
setsChildPortSpot: false,
// after the tree layout, change the width of each node so that all
// of the nodes have widths such that the collection has a given width
// commitNodes: function () {
// // overriding TreeLayout.commitNodes
// go.TreeLayout.prototype.commitNodes();
// if (ROUTINGSTYLE === "ToGroup") {
// updateNodeWidths(group, group.data.width || 100);
// }
// },
}
);
}
diagram.groupTemplate = $(
go.Group,
"Auto",
{ deletable: false, layout: makeGroupLayout() },
new go.Binding("position", "xy", go.Point.parse).makeTwoWay(
go.Point.stringify
),
new go.Binding("layout", "width", makeGroupLayout),
$(go.Shape, { fill: "white", stroke: "lightgray" }),
$(
go.Panel,
"Vertical",
{ defaultAlignment: go.Spot.Left },
$(
go.TextBlock,
{ font: "bold 14pt sans-serif", margin: new go.Margin(5, 5, 0, 5) },
new go.Binding("text")
),
$(go.Placeholder, { padding: 5 })
)
);
// initialize tree on left side
var root: any = { key: 0, group: -1 };
nodeDataArray.push(root);
for (var i = 0; i < 11; ) {
i = makeTree(3, i, 17, nodeDataArray, linkDataArray, root, -1, root.key);
}
// initialize tree on right side
root = { key: 1000, group: -2 };
nodeDataArray.push(root);
for (var i = 0; i < 15; ) {
i = makeTree(3, i, 15, nodeDataArray, linkDataArray, root, -2, root.key);
}
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
// help create a random tree structure
function makeTree(
level: number,
count: number,
max: number,
nodeDataArray:
| {
isGroup: boolean;
key: number;
text: string;
xy: string;
width: number;
}[]
| { key: any; group: any }[],
linkDataArray: { from: any; to: any }[],
parentdata: { key: any; group?: any },
groupkey: number,
rootkey: number
) {
var numchildren = Math.floor(Math.random() * 10);
for (var i = 0; i < numchildren; i++) {
if (count >= max) return count;
count++;
var childdata: any = { key: rootkey + count, group: groupkey };
nodeDataArray.push(childdata);
linkDataArray.push({ from: parentdata.key, to: childdata.key });
if (level > 0 && Math.random() > 0.5) {
count = makeTree(
level - 1,
count,
max,
nodeDataArray,
linkDataArray,
childdata,
groupkey,
rootkey
);
}
}
return count;
}
return diagram;
}
function handleModelChange(changes: any) {
alert("GoJS model changed!");
}
return (
<div>
<ReactDiagram
initDiagram={initDiagram}
divClassName="diagram-component"
nodeDataArray={nodeDataArray}
linkDataArray={linkDataArray}
onModelChange={handleModelChange}
/>
</div>
);
}

Angular 8 Error "ERROR Error: "Cannot find control with name: 'getAccountArr'"" When using Form Array

I'm build in Angular 8 and I'm trying to render a list of inputs and prepopulate them with JSON data. I am using Reactive Forms for this project. The types array is being mapped correctly and being stored in the group fine. One of the things I am thinking it has something to do with the FormArrayName and where it is placed compared to the FormGroupName. I am wondering if I need to wrap the FormArray Name in the FormGroup Name.
view.ts
export class View2Component implements OnInit {
// public accountArr: FormArray;
public accounts: FormArray;
public accountForm: FormGroup;
types: any = [
{
name: "Assets",
display: {
default:'Display Value',
inputType:'input'
},
type: {
default:'type Value',
inputType:'selected',
data: ['a', 'b', 'c']
},
totalDesc: {
default:'totalDesc Value',
inputType:'input'
},
underlineBefore: {
default:'underlineBefore Value',
inputType:'input'
},
underlineAfter: {
default:'underlineAfter Value',
inputType:'input'
},
reverse: {
default: false,
inputType:'checkbox'
},
print: {
default: true,
inputType:'checkbox'
},
},
{
name: "Assets",
display: {
default:'Display Value',
inputType:'input'
},
type: {
default:'type Value',
inputType:'selected',
data: ['a', 'b', 'c']
},
totalDesc: {
default:'totalDesc Value',
inputType:'input'
},
underlineBefore: {
default:'underlineBefore Value',
inputType:'input'
},
underlineAfter: {
default:'underlineAfter Value',
inputType:'input'
},
reverse: {
default: false,
inputType:'checkbox'
},
print: {
default: true,
inputType:'checkbox'
},
}
];
get getAccountArr() { return this.accountForm.get('accounts') as FormArray; }
constructor(private fb: FormBuilder) {
}
ngOnInit() {
this.accountForm = this.fb.group({
accounts: this.fb.array([])
});
this.renderAccountForm();
console.log('accountArr', this.accountForm);
}
renderAccountForm() {
this.types.map(item => {
let val = this.fb.group({
display: [item.display.default],
type: [item.type.default]
});
this.getAccountArr.push(val);
});
}
}
view.html
<form [formGroup]="accountForm" novalidate>
<div formArrayName="getAccountArr" style="height:100px; margin:40px auto;">
<div *ngFor="let account of getAccountArr.controls; let i = index;">
<div [formGroupName]="i">
<h1 class="index-class">{{ i }}</h1>
<h1>{{ account.value.display }}</h1>
<input
type="text"
formControlName="{{ i }}"
[value]="account.value.display"
[placeholder]="account.value.displays"
/>
</div>
</div>
</div>
</form>
You are using getter incorrectly.
Replace get getAccountArr() { return this.accountForm.get('accounts') as FormArray; }
with get accounts() { return this.accountForm.get('accounts') as FormArray; }
and remove public accounts: FormArray;
get acts as an 'alias' and everytime you refer to this.accounts will return the FormArray.
You need to replace this line
<div formArrayName="getAccountArr" style="height:100px; margin:40px auto;">
with
<div formArrayName="accounts" style="height:100px; margin:40px auto;">
You need to give the name of the FormArray to formArrayName directive, and getAccountArr is not an exisiting form array in your form group, it's just a property that returns your form array which is different

How to add dynamic property in angularjs object

I have object
$scope.postData = {
'pmu.messages.message': $scope.upd.message,
'pmu.received.id': $scope.upd.atomByReceivedBy.id,
};
and in scope there is $scope.ImageList which contains image path array
[
{ img: 'a.jpg', smallimg: 'b.jpg', smpath: 'c.jpg' },
{ img: 'a1.jpg', smallimg: 'b1.jpg', smpath: 'c1.jpg' },
];
I want to add these array value to $scope.postData field property value
as
$scope.postData = {
'pmu.messages.message': $scope.upd.message,
'pmu.received.id': $scope.upd.atomByReceivedBy.id,
'pmu.image[0].img':
'pmu.image[0].smallimg':
'pmu.image[0].smimg':
'pmu.image[1].img':
'pmu.image[1].smallimg':
'pmu.image[1].smimg':
}
How to achieve this?
Iterate $scope.ImageList and use Bracket Notation to create properties.
Here in the example, I have used just one property
$scope.ImageList.forEach(function(element, index){
$scope.postData['pmu.image[' + index +'].img'] = element.img;
});
You can iterate the array with Array.forEach, and then extract the key & value of each image using Object.entries.
$scope = {
upd: {
message: '',
atomByReceivedBy: {
id: ''
}
},
ImageList: [{
img: 'a.jpg',
smallimg: 'b.jpg',
smpath: 'c.jpg'
},
{
img: 'a1.jpg',
smallimg: 'b1.jpg',
smpath: 'c1.jpg'
},
]
}
$scope.postData = {
'pmu.messages.message': $scope.upd.message,
'pmu.received.id': $scope.upd.atomByReceivedBy.id,
};
$scope.ImageList.forEach((img, index) => {
Object.entries(img).forEach(([key, value]) => {
$scope.postData[`pmu.image[${index}].${key}`] = value;
});
});
console.log($scope.postData);

Vue.js filtering on array

I am trying to filter an array using a computed property in vue.js. I would like to search on on multiple fields, name, state, tags etc.
My data:
events: [
{
id: 1,
name: 'Name of event',
url: '#',
datetime: '2017-05-10T00:00:00Z',
description: 'The full text of the event',
state: 'VIC',
tags: [
'ordinary',
'advanced'
]
},
{
id: 2,
name: 'Another event',
url: '#',
datetime: '2017-05-12T00:00:00Z',
description: 'The full text of the event',
state: 'VIC',
tags: [
'beginner'
]
},
{
id: 3,
name: 'Great event',
url: '#',
datetime: '2017-05-18T00:00:00Z',
description: 'The full text of the event',
state: 'NSW',
tags: [
'beginner'
]
}
]
},
The following function works as expected, however I cant work out how to have it search the items in 'tags' (commented out).
searchevents: function(){
let result = this.events
if (this.filterValue){
result = result.filter(event =>
event.name.toLowerCase().includes(this.filterValue.toLowerCase()) ||
event.state.toLowerCase().includes(this.filterValue.toLowerCase())
// event.tags.toLowerCase().values().includes(this.filterValue.toLowerCase())
)
}
return result
}
The following returns a blank array, this method works ok when i have done it in angular but not in vue.
searchevents2: function(){
var searchRegex = new RegExp(this.filterValue,'i')
this.events.filter(function(event){
return !self.filterValue || searchRegex.test(event.name) || searchRegex.test(event.state)
})
}
Ideally I would either like to be able to list array items to filter by or just filter by the entire array.
Appreciate any help, first post here so be gentle. I have a lot more experience with Python than Javascript so i may also use incorrect terminology at times.
You weren't too far off.
For your searchEvents filter, you just needed to add the tag filter. Here's how you might do that.
searchevents: function(){
let result = this.events
if (!this.filterValue)
return result
const filterValue = this.filterValue.toLowerCase()
const filter = event =>
event.name.toLowerCase().includes(filterValue) ||
event.state.toLowerCase().includes(filterValue) ||
event.tags.some(tag => tag.toLowerCase().includes(filterValue))
return result.filter(filter)
}
Array.some() is a standard array method that returns true if any element of the array passes your test.
searchevents2: function(){
const searchRegex = new RegExp(this.filterValue,'i')
return this.events.filter(event =>
!this.filterValue || searchRegex.test(event.name) || searchRegex.test(event.state))
}
With searchEvents2 you really only left an errant self in there. Either you needed to set self before you executed the filter, or, as I have done here, turned it into an arrow function.
Example.
const app = new Vue ({
el: '#app',
data: {
search: '',
userList: [
{
id: 1,
name: "Prem"
},
{
id: 1,
name: "Chandu"
},
{
id: 1,
name: "Shravya"
}
]
},
computed: {
filteredAndSorted(){
// function to compare names
function compare(a, b) {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
}
return this.userList.filter(user => {
return user.name.toLowerCase().includes(this.search.toLowerCase())
}).sort(compare)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="app">
<div class="search-wrapper">
<input type="text" v-model="search" placeholder="Search title.."/>
<label>Search Users:</label>
</div>
<ul>
<li v-for="user in filteredAndSorted">{{user.name}}</li>
</ul>
</div>

JSON re-grouping with for each method in AngularJs

I am new in angular and i want to re-group a JSON. Is it possible to do with angular.forEach() method?
[
{
"doc":{
"Title":"Main",
"name":"Ajith",
"Day":"03",
"count":3
}
},
{
"doc":{
"Title":"starters",
"name":"Saji",
"Day":"01",
"count":39
}
},
{
"doc":{
"Title":"desert",
"name":"Sajeeb",
"Day":"02",
"count":63
}
},
{
"doc":{
"Title":"Main",
"name":"Suith",
"Day":"03",
"count":3
}
},
{
"doc":{
"Title":"starters",
"name":"Manu",
"Day":"01",
"count":9
}
}
]
I want the output should be like following.
{
"order":[
{
"Day":"01",
"Title":"starters",
"items":[
{
"name":"Saji",
"count":39
},
{
"name":"Manu",
"count":9
}
]
},
{
"Day":"02",
"Title":"desert",
"items":[
{
"name":"Sajeeb",
"count":63
}
]
},
{
"Day":"03",
"Title":"Main",
"items":[
{
"name":"Ajith",
"count":3
},
{
"name":"Suith",
"count":3
}
]
}
]
}
To regroup with angular.forEach() method.
Please help. Thanks.
https://jsfiddle.net/9fwtm0a0/4/
var days = [];
arr.forEach(function(d) {
var day = parseInt(d.doc.Day);
var item = { name: d.doc.name, count: d.doc.count };
if (days[day])
days[day].items.push(item);
else
days[day] = { Day: '0' + day, Title: d.doc.Title, items: item]};
});
days.sort(function (x, y) {
return parseInt(x.Day) > parseInt(y.Day);
});
var eventsinOrder = { order: days };
You don't really need the Angular forEach but you can easily substitute it in:
var days = [];
angular.forEach(arr, function (val, key) {
var day = parseInt(val.doc.Day);
var item = { name: val.doc.name, count: val.doc.count };
if (days[day])
days[day].items.push(item);
else
days[day] = { Day: '0' + day, Title: val.doc.Title, items: [item]};
});
days.sort(function (x, y) {
return parseInt(x.Day) > parseInt(y.Day);
});
var eventsinOrder = { order: days };
Either way you go you will still need to make use of the sort function (or any equivalent) to perform the sorting while you're building this resultant object.

Resources