Dynamically splitting strings with multiple keywords - javascript

I'm working on headless CMS using Angular.
In the content below, whatever is wrapped inside {{ }} is considered to be an anchor link.
We processes your data in accordance with the {{policy_placeholder}}. You have the right to object to the processing of your personal data. Check “Your rights” in the {{policy_placeholder}} and {{term_policy}} for more information.;`
So with the above example, below is the expected result
[
{
type: "TEXT",
content: "We processes your data in accordance with the "
},
{
type: "LINK",
content: "{{policy_placeholder}}"
},
{
type: "TEXT",
content:
". You have the right to object to the processing of your personal data. Check “Your rights” in the "
},
{
type: "LINK",
content: "{{policy_placeholder}}"
},
{
type: "TEXT",
content: " and "
},
{
type: "LINK",
content: "{{terms_placeholder}}"
},
{
type: "TEXT",
content: " for more information."
}
];
Below is what I tried
splitString = function(string, splitters) {
var list = [string];
for(var i=0, len=splitters.length; i<len; i++) {
traverseList(list, splitters[i], 0);
}
const x = flatten(list);
console.log(x);
return flatten(list);
}
traverseList = function(list, splitter, index) {
if(list[index]) {
if((list.constructor !== String) && (list[index].constructor === String))
(list[index] != list[index].split(splitter)) ? list[index] = list[index].split(splitter) : null;
(list[index].constructor === Array) ? traverseList(list[index], splitter, 0) : null;
(list.constructor === Array) ? traverseList(list, splitter, index+1) : null;
}
}
flatten = function(arr) {
return arr.reduce(function(acc, val) {
return acc.concat(val.constructor === Array ? flatten(val) : val);
},[]);
}
var splitList = ["{{policy_placeholder}}", "{{term_policy}}"];
splitString(source, splitList);
The issue is that I've to manually add the splitList, but i want to make it dynamic based on {{ }}
How can this be done?

When you spread a string, you actually split it into characters.
const source = `We processes your data in accordance with the {{policy_placeholder1}}. You have the right to object to the processing of your personal data. Check “Your rights” in the {{policy_placeholder2}} for more information.`;
function splitString(str) {
const ans = [];
const linkTokenRegex = /\{\{.+?\}\}/g;
const textsArr = str.split(linkTokenRegex);
const linksArr = str.match(linkTokenRegex);
textsArr.forEach((textPart, index) => {
ans.push({
type: "TEXT",
content: textPart,
});
if (linksArr[index]) {
ans.push({
type: "LINK",
content: linksArr[index],
});
}
});
return ans;
}
console.log(splitString(source));

Related

duplicate player name should not be in the same array

I am making a matchmaking system where 2 players of the same level will be matched.
My target is how can I remove a duplicate player name in the same
array? After removing that duplicate entry, it should be stored on
another array(waiting list).
I have provided an example and a screenshot below. Any help will be appreciated. Thank you.
script for matching: (Matching the players with the same level)
const combine = (source) => {
return source.reduce((acc, curr) => {
if (acc[curr.level]) {
const levelArr = acc[curr.level];
const last = levelArr[levelArr.length - 1];
if (last.length === 2) {
levelArr.push([curr])
} else {
last.push(curr)
}
} else {
acc[curr.level] = [
[curr]
];
}
return acc;
}, {})
};
script ajax: (Fetching data from db)
let ajaxResult = []; // the pushed data will be saved here
let save_method;
let table;
let base_url = "<?php echo base_url();?>";
let result = [];
var html = "";
$(document).ready(function() {
//datatables
table = $("#entry_list1").DataTable({
processing: false,
serverSide: true,
order: [],
searching: false,
paging: false,
info: false,
ajax: {
url: "<?php echo site_url('controller/fetch')?>",
type: "POST",
async: true,
dataType: "json",
success: function(data) {
result = combine(data.data2);
console.log(result)
var keys = Object.keys(result)
for (var i = 0; i < keys.length; i++) {
result[keys[i]].forEach(function(val) {
val.forEach(function(value, index) {
var entryIDs = index == 0 ? "entryIDM[]" : "entryIDW[]"
var players = index == 0 ? "playerM[]" : "playerW[]"
var levels = index == 0 ? "levelM[]" : "levelW[]"
html += `<input type="text" name="${entryIDs}" value="${value.entryID}">
<input type="text" name="${players}" value="${value.player}">
<input type="text" name="${levels}" value="${value.level}">
`
})
})
}
document.getElementById("result").innerHTML = html //add html to div
},
},
"columnDefs": [{
"targets": [0], //first column
"orderable": false, //set not orderable
},
{
"targets": [-1], //last column
"orderable": false, //set not orderable
},
],
});
});
I would suggest removing duplicate players before passing them to the combine function. This way you shouldn't see duplicate players matched.
I would create a removeDuplicates function to remove duplicate players from the player list.
In this example, we have 18 players, with each player duplicated once.
We run these through the removeDuplicates function, now we have 9 unique players.
We then pass the unique players to the combine function and get 3 groups of 2 matched players and 3 unmatched players.
const players = Array.from({ length: 18 }, (v,k) => ( { level: Math.floor(k / 6) + 1, player: `test-${Math.floor(k/2)+1}` }));
function removeDuplicates(players) {
return Object.values(players.reduce((acc, curr) => {
acc[curr.player] = acc[curr.player] || curr;
return acc;
}, {}))
}
const combine = (source) => {
return source.reduce((acc, curr) => {
if (acc[curr.level]) {
const levelArr = acc[curr.level];
const last = levelArr[levelArr.length - 1];
if (last.length === 2) {
levelArr.push([curr])
} else {
last.push(curr)
}
} else {
acc[curr.level] = [
[curr]
];
}
return acc;
}, {})
};
console.log("All players:", players);
const uniquePlayers = removeDuplicates(players);
console.log("Unique players:", uniquePlayers);
const matched = Object.values(combine(uniquePlayers)).flatMap(a => a.filter(x => x.length === 2));
const unmatched = Object.values(combine(uniquePlayers)).flatMap(a => a.filter(x => x.length === 1));
console.log("Matched players:", matched);
console.log("Unmatched players:", unmatched);

Better way to map a deep object to new object

This code works for converting the JSON to an object where each name object turns into the key for either its value, or if it instead has its own element object breaks that out and does the same to its contents.
Is there a better way to do this that would also allow for more extensiblity of the JSON schema?
Is there a way I can get it all down to a simpler function that I can pass the first element and have it convert it down to whatever depth the schema goes?
const fs = require('fs');
{
let scheme = JSON.parse('{"$schema":{"root":{"name":"THINGY","dtd":{"name":"DOCTYPE","value":"something.dtd","commentBefore":["?xml version='1.0'?","Version NULL"]},"ele":{"name":"REPORT","ele":[{"name":"SEGMENT0","ele":[{"name":"NUMBER1","value":""},{"name":"NUMBER2","value":""}]},{"name":"SEGMENT1","ele":[{"name":"RECORD1","ele":[{"name":"NUMBER1","value":""},{"name":"NUMBER2","value":""}]}]},{"name":"SEGMENT2","ele":[]},{"name":"SEGMENT3","ele":[]},{"name":"SEGMENT4","ele":[]},{"name":"SEGMENT5","ele":[]}]}}}}').$schema.root;
let depth = 0;
var compiled = {
[scheme.ele.name]: scheme.ele.ele.map(function(i) {
if (typeof i.ele != 'undefined') {
return {
[i.name]: i.ele.map(function(k) {
if (typeof k.ele != 'undefined') {
return {
[k.name]: k.ele.map(function(p) {
if (typeof p.ele != 'undefined') {
return {
[p.name]: p.ele
};
} else {
return {
[p.name]: p.value
};
}
})
};
} else {
return {
[k.name]: k.value
};
}
})
};
} else {
return {
[i.name]: i.value
};
}
})
};
}
console.log(JSON.stringify(compiled, 0, 2));
I should add, this is intended to eventually also apply validation and grab real data when it gets to the string objects.
The output looks like this:
{
"REPORT": [
{
"SEGMENT0": [
{
"NUMBER1": ""
},
{
"NUMBER2": ""
}
]
},
{
"SEGMENT1": [
{
"RECORD1": [
{
"NUMBER1": ""
},
{
"NUMBER2": ""
}
]
}
]
},
{
"SEGMENT2": []
},
{
"SEGMENT3": []
},
{
"SEGMENT4": []
},
{
"SEGMENT5": []
}
]
}
You could destructure the object, get name, ele and value and return a new object with name as key and either an array by mapping the objects of ele or the value.
const
getData = ({ name, ele, value }) => ({
[name]: Array.isArray(ele)
? ele.map(getData)
: value
});
var scheme = JSON.parse('{"$schema":{"root":{"name":"THINGY","dtd":{"name":"DOCTYPE","value":"something.dtd","commentBefore":["?xml version=\'1.0\'?","Version NULL"]},"ele":{"name":"REPORT","ele":[{"name":"SEGMENT0","ele":[{"name":"NUMBER1","value":""},{"name":"NUMBER2","value":""}]},{"name":"SEGMENT1","ele":[{"name":"RECORD1","ele":[{"name":"NUMBER1","value":""},{"name":"NUMBER2","value":""}]}]},{"name":"SEGMENT2","ele":[]},{"name":"SEGMENT3","ele":[]},{"name":"SEGMENT4","ele":[]},{"name":"SEGMENT5","ele":[]}]}}}}').$schema.root,
result = getData(scheme.ele);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina's answer is cleaner but this looks a bit more like your code so I figured I'd post it anyway.
let scheme = JSON.parse('{"$schema":{"root":{"name":"THINGY","dtd":{"name":"DOCTYPE","value":"something.dtd","commentBefore":["?xml version=\'1.0 \'?","Version NULL"]},"ele":{"name":"REPORT","ele":[{"name":"SEGMENT0","ele":[{"name":"NUMBER1","value":""},{"name":"NUMBER2","value":"1"}]},{"name":"SEGMENT1","ele":[{"name":"RECORD1","ele":[{"name":"NUMBER1","value":"2"},{"name":"NUMBER2","value":""}]}]},{"name":"SEGMENT2","ele":[]},{"name":"SEGMENT3","ele":[]},{"name":"SEGMENT4","ele":[]},{"name":"SEGMENT5","ele":[]}]}}}}').$schema.root;
let newScheme = JSON.parse('{"$schema":{"root":{"name":"THINGY","dtd":{"name":"DOCTYPE","value":"something.dtd","commentBefore":["?xml version=\'1.0 \'?","Version NULL"]},"ele":{"name":"REPORT","ele":[{"name":"SEGMENT0","ele":[{"name":"NUMBER1","value":"1"},{"name":"NUMBER2","value":"3"}]},{"name":"SEGMENT1","ele":[{"name":"RECORD1","ele":[{"name":"NUMBER1","value":"4"},{"name":"NUMBER2","value":""}]}]},{"name":"SEGMENT2","ele":[]},{"name":"SEGMENT3","ele":[]},{"name":"SEGMENT4","ele":[]},{"name":"SEGMENT5","ele":[]}]}}}}').$schema.root;
//Yay, recursion!
function mapObj(a, o = {}) {
let array = o[a.name] || [];
for (let i = 0; i < a.ele.length; i++) {
let b = a.ele[i];
array[i] = b.ele ?
mapObj(b, array[i]) : {
[b.name]: b.value
};
}
o[a.name] = array;
return o;
}
let obj = mapObj(scheme.ele);
console.log(obj);
console.log(mapObj(newScheme.ele, obj));

Matching a string to one of many patterns and extracting data

I have a problem I want to solve with RegEx, or any other method if there is a better one. I've tried several ways to achieve the goal, but nothing really worked.
I have an array with endpoints:
const endpoints = [
{
id: 1,
url: "/api/items/:itemId"
},
{
id: 2,
url: "/api/users/:userName/delete"
},
{
id: 3,
url: "/api/users/:userName/edit"
}
];
And a request URL:
const url = "/api/users/max/edit";
Now what I want is to have a function which acts like this:
const rewrite = (url, endpoints) => {
// What is the best way to achieve the following return value:
return {
endpointId: 3,
values: {
userName: "max"
}
};
};
Explanation: The function should find the appropriate endpoint for the url. All parts of the endpoint url which start with a colon are not static, but should rather be replaced with values from the request url. In this case :userName should be replaced with max.
I've been in web development for some time now, but to be honest I've almost no clue how to solve such a problem.
const rewrite = (url, endpoints) => {
var doubledArray = Array.prototype.map.call(endpoints, function(el) {
return {
id: el.id,
url: el.url.split('/')
};
});
var parts = url.split('/');
var i = 0;
parts.forEach(function(element) {
doubledArray = doubledArray.filter(el => (element == el.url[i] || el.url[i].startsWith(':')));
i++;
});
return {
endpointId: doubledArray[0].id,
values: {
[`${doubledArray[0].url.filter(el => el.startsWith(':'))[0].substring(1)}`]: parts[doubledArray[0].url.findIndex(function (el) { return el.startsWith(':'); } )],
}
};
};
You can go through the endpoints making each .url into a RegExp to test the url against.
When a matching one is found, it is just a matter of extracting the needed part and making up an Object with the property name:
<script>
const myEndpoints = [
{
id: 1,
url: "/api/items/:itemId"
},
{
id: 2,
url: "/api/users/:userName/delete"
},
{
id: 3,
url: "/api/users/:userName/edit"
}
];
const myUrl = "/api/users/nermal/edit";
const rewrite = (url, endpoints) => {
for (let i = 0; i < endpoints.length; i++) {
var rep = new RegExp(":(\\w+)", "m");
var propName = rep.exec(endpoints[i].url);
var reu = new RegExp(endpoints[i].url.replace(propName[0], "(.*)"));
var a = reu.exec(url);
if (a !== null) {
var x = new Object;
x["endpointId"] = endpoints[i].id;
var y = new Object;
y[propName[1]] = a[1];
x["values"] = y;
return x;
}
}
return null;
};
var q = rewrite(myUrl, myEndpoints);
console.log(q);
console.log(q.values);
</script>
Outputs:
Object { endpointId: 3, values: {…} }
Object { userName: "nermal" }

Customizing easyAutoComplete

var options = {
url: function (phrase) {
return "location/autoComplete?key=" + phrase;
},
getValue: "cityName",
template: {
type: "custom",
method: function (value, item) {
return value + ", " + item.stateName;
}
},
list: {
onClickEvent: function () {
var selectedLongitude = $("#autoCompleteSearch").getSelectedItemData().longitude;
var selectedLatitude = $("#autoCompleteSearch").getSelectedItemData().latitude;
userPos = {
lat: Number(selectedLatitude),
lng: Number(selectedLongitude)
};
map.setCenter(userPos);
map.setZoom(17)
},
showAnimation: {
type: "fade", //normal|slide|fade
time: 400,
callback: function () {
}
},
hideAnimation: {
type: "slide", //normal|slide|fade
time: 400,
callback: function () {
}
}
}
};
This is the code I am using right now to fetch a remote webservice data. Now I need to get the data and remove the duplicate "city names". Also I need to show "Data not available" or some custom method when there is no data from the webservice. I am kind of stuck. Would be great if someone could point me on how to achieve this.
You can get rid of duplicates using a helper function:
function removeDuplicates(input) {
if (!input) { //Falsy input
return input;
}
var isArray = Array.isArray(input) ? [] : {};
var items = [];
var output = {};
for (var key in input) {
if (items.indexOf(input[key]) < 0) {
items.push(input[key]);
if (!isArray) {
output[key] = input[key];
}
}
}
return isArray ? items : output;
}
You can show your custom text if Array.isArray(input) ? (input.length === 0) : (Object.keys(input).length === 0 && input.constructor === Object).

Semantic UI - Search API: how I can highlight certain characters that match the query?

I have a problem when I want to highlight certain characters that match the query in search module.
an example is like this forum: https://forums.meteor.com
Previously, I already asked this issue at https://github.com/Semantic-Org/Semantic-UI/issues/4930#issuecomment-275011130, but until now not been resolved.
here is my current demo: https://jsfiddle.net/agaust/5854gae9/4/
$(document).ready(function() {
$.fn.api.settings.api = {
'search': 'https://gist.githubusercontent.com/agusmakmun/e258a3243367105c32919c083eb577fe/raw/8b0038b0141f5813c03baece0b2730e17216f8c9/result-data.json?q={query}'
};
$('.ui.search.quick-search').search({
type : 'category',
minCharacters : 1, // just under devel to make it easy
apiSettings : {
action: 'search',
onResponse: function(searchApiResponse) {
var response = {
results : {},
//action : searchApiResponse.action // dict action
};
// translate `searchApiResponse` response to work with search
var numbThreads = 0, numbUsers = 0;
$.each(searchApiResponse.items, function(index, item) {
var
model = item.model || 'Unknown',
maxResults = 6,
maxItems = maxResults/2 // max results per-Category
;
if(index >= maxResults) {
return false;
}
// create new model category
if(response.results[model] === undefined) {
response.results[model] = {
name : model,
results : []
};
}
if(item.model === 'Threads') {
if ((numbThreads < maxItems) || (numbUsers < numbThreads)) {
response.results[model].results.push({
title: item.title,
description: item.description,
url: item.url
});
}
numbThreads++;
}else if (item.model === 'Users') {
if ((numbUsers < maxItems) || (numbThreads < numbUsers)) {
response.results[model].results.push({
title: item.username,
description: item.username,
url: item.profile_url,
image: item.gravatar_url,
price: item.total_threads
});
}
numbUsers++;
}
});
// only showing the bottom action button if under limited
if (searchApiResponse.limited_items) {
response["action"] = searchApiResponse.action;
}
console.log(response);
return response;
}
}
});
});
any help would be apreciated.. :)

Categories

Resources