Reduce URL and label for breadcrumbs - javascript

I have
const url = "/aaa/222/ccc"
const label = "/aaa/bbb/ccc"
I need to show this in breadcrumbs with labels, but when click on /bbb call 222
I try to handle this
const url = "/aaa/2222/ccc"
const label = "/aaa/bbb/ccc"
breadcrumbs: any;
this.breadcrumbs = url.split('/')
.reduce((acc, cur, i) => {
const url1 = i === 0
? `${acc[i - 1].url1}/${label.split('/')[i]}`
: undefined;
const label1 = i === 0
? `${acc[i - 1].label1}/${url.split('/')[i]}`
: undefined;
const breadcrumb = {
url1,
label1
};
acc.push(breadcrumb);
return acc;
}, []);
var numbers = [175, 50, 25];
But this is output, and this is not correct
/aaa/bbb/ccc/aaa/bbb/ccc/aaa/sites/bbb/ccc/aaa/222
I need in UI aaa/bbb/ccc but in background aaa/222/ccc
Thnx

It looks like you want to build up an array of objects containing a label and url section.
The following would do that for you.
const url = "/aaa/222/ccc"
const label = "/aaa/bbb/ccc"
createObject(url,label) {
const urlArray = url.split("/").slice(1);
const labelArray = label.split("/").slice(1);
return urlArray.map((item, index) => {
return {url: item, label: labelArray[index]}
})
}

Look up the previous accumulated url/label if i > 0, not when i === 0 in your OP.
const url = "/aaa/2222/ccc";
const label = "/aaa/bbb/ccc";
const urlPaths = url.split("/");
const labelPaths = label.split("/");
const snackbar = urlPaths.reduce((acc, urlPath, i) => {
let path;
if (i === 0) {
path = {
url: "",
label: "",
};
} else {
const { url, label } = acc[i-1];
path = {
url: `${url}/${urlPath}`,
label: `${label}/${labelPaths[i]}`
};
}
acc.push(path);
return acc;
}, []).splice(1);
console.log(snackbar);
Outputs
[{
"url": "/aaa",
"label": "/aaa"
}, {
"url": "/aaa/2222",
"label": "/aaa/bbb"
}, {
"url": "/aaa/2222/ccc",
"label": "/aaa/bbb/ccc"
}]
EDIT: the .splice(1) trailing call removes the first snackbar path (which has empty strings for the URL/label).

Related

How can I add View count and Subscriber Count?

This Google Apps Script code Search YouTube results by keywords. I want to add View Count and Subscribes Count too.
Output Data
function youTubeSearchResults() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const values = sheet.getRange("A2:A" + sheet.getLastRow()).getValues();
const modifyResults = values.flatMap(([keywords]) => {
const searchResults = YouTube.Search.list("id, snippet", { q: keywords, maxResults: 10, type: "video", order: "viewCount", videoDuration: "short", order: "date" });
const fSearchResults = searchResults.items.filter(function (sr) { return sr.id.kind === "youtube#video" });
return fSearchResults.map(function (sr) { return [keywords, sr.id.videoId, `https://www.youtube.com/watch?v=${sr.id.videoId}`, sr.snippet.title, sr.snippet.publishedAt, sr.snippet.channelTitle, sr.snippet.channelId,`https://www.youtube.com/channel/${sr.snippet.channelId}`, sr.snippet.thumbnails.high.url] });
});
sheet.getRange(2, 2, modifyResults.length, modifyResults[0].length).setValues(modifyResults);
}
When your showing script is modified, how about the following modification?
Modified script:
function youTubeSearchResults() {
// 1. Retrieve values from column "A".
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const values = sheet.getRange("A2:A" + sheet.getLastRow()).getDisplayValues().filter(([a]) => a);
// 2. Retrieve your current values.
const modifyResults = values.flatMap(([keywords]) => {
const searchResults = YouTube.Search.list("id, snippet", { q: keywords, maxResults: 10, type: "video", order: "viewCount", videoDuration: "short", order: "date" });
const fSearchResults = searchResults.items.filter(function (sr) { return sr.id.kind === "youtube#video" });
return fSearchResults.map(function (sr) { return [keywords, sr.id.videoId, `https://www.youtube.com/watch?v=${sr.id.videoId}`, sr.snippet.title, sr.snippet.publishedAt, sr.snippet.channelTitle, sr.snippet.channelId, `https://www.youtube.com/channel/${sr.snippet.channelId}`, sr.snippet.thumbnails.high.url] });
});
// 3. Retrieve viewCounts and subscriberCounts.
const { videoIds, channelIds } = modifyResults.reduce((o, r) => {
o.videoIds.push(r[1]);
o.channelIds.push(r[6]);
return o;
}, { videoIds: [], channelIds: [] });
const limit = 50;
const { viewCounts, subscriberCounts } = [...Array(Math.ceil(videoIds.length / limit))].reduce((obj, _) => {
const vIds = videoIds.splice(0, limit);
const cIds = channelIds.splice(0, limit);
const res1 = YouTube.Videos.list(["statistics"], { id: vIds, maxResults: limit }).items.map(({ statistics: { viewCount } }) => viewCount);
const obj2 = YouTube.Channels.list(["statistics"], { id: cIds, maxResults: limit }).items.reduce((o, { id, statistics: { subscriberCount } }) => (o[id] = subscriberCount, o), {});
const res2 = cIds.map(e => obj2[e] || null);
obj.viewCounts = [...obj.viewCounts, ...res1];
obj.subscriberCounts = [...obj.subscriberCounts, ...res2];
return obj;
}, { viewCounts: [], subscriberCounts: [] });
const ar = [viewCounts, subscriberCounts];
const rr = ar[0].map((_, c) => ar.map(r => r[c]));
// 4. Merge data.
const res = modifyResults.map((r, i) => [...r, ...rr[i]]);
// 5. Put values on Spreadsheet.
sheet.getRange(2, 2, res.length, res[0].length).setValues(res);
}
When this script is run, the following flow is run.
Retrieve values from column "A".
Retrieve your current values.
Retrieve "viewCounts" and "subscriberCounts".
Merge data.
Put values on Spreadsheet.
References:
Videos: list
Channels: list
reduce()

Could not parse the remainder: '${profiles['user']}' from '${profiles['user']}

const data = '{{ search_user }}'
const rdata = JSON.parse(data.replace(/"/g, '"'))
const input = document.getElementById('user-input_products')
let filteredArr = []
input.addEventListener('keyup', (e)=>{
box.innerHTML = ""
filterMax = (fn, c) => x => c && fn(x) && c--
filter = profiles=> profiles['full_name'].includes(e.target.value)
max = 30
filteredArr = rdata.filter(filterMax(filter, max))
if (filteredArr.length > 0) {
filteredArr.map(profiles=>{
box.innerHTML += `<li>${profiles['full_name']}</li>`
})
} else {
box.innerHTML = "No result found"
}
})
And when i want to pass id to django url inside of innerHTML it's give me this error:
Could not parse the remainder: '${profiles["user"]}' from '${profiles["user"]}'
How can i fix that?
rdata:
[
{
"user": 28,
"full_name": "John"
},
{
"user": 35,
"full_name": "Robert"
},
{
"user": 37,
"full_name": "Mary"
},
{
"user": 38,
"full_name": "Jennifer"
},
]
You cannot mix server side Python code with frontend javascript code like this. Django Templates sees ${profiles['user']} as a simple string. To fix this you can store a path prefix in a JS variable and combine the user links with JS template literal:
const path_prefix = "{% url 'user_num' %}"
const data = '{{ search_user }}'
const rdata = JSON.parse(data.replace(/"/g, '"'))
const input = document.getElementById('user-input_products')
let filteredArr = []
input.addEventListener('keyup', (e)=>{
box.innerHTML = ""
filterMax = (fn, c) => x => c && fn(x) && c--
filter = profiles=> profiles['full_name'].includes(e.target.value)
max = 30
filteredArr = rdata.filter(filterMax(filter, max))
if (filteredArr.length > 0) {
filteredArr.map(profiles=>{
box.innerHTML += `<li>${profiles['full_name']}</li>`
})
} else {
box.innerHTML = "No result found"
}
})
If the path for the user_num view function is user_num/, then ${path_prefix}${profiles['user']} will produce e.g. user_num/28. You may have to create a new URL definition if user_num function requires an argument, or you can just put in the actual path directly, e.g.: user_num/${profiles['user']}.

Need to convert the array of filepaths into treeview json object

Need to convert the array of file paths into Treeview JSON object
Array Data:
[path1/subpath1/file1.doc",
"path1/subpath1/file2.doc",
"path1/subpath2/file1.doc",
"path1/subpath2/file2.doc",
"path2/subpath1/file1.doc",
"path2/subpath1/file2.doc",
"path2/subpath2/file1.doc",
"path2/subpath2/file2.doc",
"path2/subpath2/additionalpath1/file1.doc"]
I want below object Result:
{
"title": "path1",
"childNodes" : [
{ "title":"subpath1", "childNodes":[{ "title":"file1.doc", "childNodes":[] }] },
{ "title":"subpath2", "childNodes":[{ "title":"file1.doc", "childNodes":[] }] }
]
}
I was able to convert it into an object using the below code snippet but not able to transform the way I want it
let treePath = {};
let formattedData = {};
data.forEach(path => {
let levels = path.split("/");
let file = levels.pop();
let prevLevel = treePath;
let prevProp = levels.shift();
levels.forEach(prop => {
prevLevel[prevProp] = prevLevel[prevProp] || {};
prevLevel = prevLevel[prevProp];
prevProp = prop;
});
prevLevel[prevProp] = (prevLevel[prevProp] || []).concat([file]);
});
How can i do this????
You could reduce the parts of pathes and search for same title.
const
pathes = ["path1/subpath1/file1.doc", "path1/subpath1/file2.doc", "path1/subpath2/file1.doc", "path1/subpath2/file2.doc", "path2/subpath1/file1.doc", "path2/subpath1/file2.doc", "path2/subpath2/file1.doc", "path2/subpath2/file2.doc", "path2/subpath2/additionalpath1/file1.doc"],
result = pathes.reduce((r, path) => {
path.split('/').reduce((childNodes, title) => {
let child = childNodes.find(n => n.title === title);
if (!child) childNodes.push(child = { title, childNodes: [] });
return child.childNodes;
}, r);
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to update async await function when a variable change?

genderPie()
let filter = {};
async function genderPie() {
const d = await getData();
const g = await d.reduce((a, o) => (o.GEN && a.push(o.GEN), a), []);
const gender = Object.keys(g).length;
const m = await d.reduce((a, o) => (o.GEN == 1 && a.push(o.GEN), a), []);
const male = Object.keys(m).length;
const f = await d.reduce((a, o) => (o.GEN == 2 && a.push(o.GEN), a), []);
const female = Object.keys(f).length;
var data = [{
name: 'male',
y: male,
id: 1
}, {
name: 'female',
y: female,
id: 2
}];
chart = new Highcharts.Chart({
plotOptions: {
pie: {
innerSize: '80%',
dataLabels: {
connectorWidth: 0
}
}
},
series: [{
"data": data,
type: 'pie',
animation: false,
point: {
events: {
click: function(event) {
filter.GEN = '' + this.id + '';
}
}
}
}],
"chart": {
"renderTo": "gender"
},
});
}
async function getData() {
buildFilter = (filter) => {
let query = {};
for (let keys in filter) {
if (filter[keys].constructor === Array && filter[keys].length > 0) {
query[keys] = filter[keys];
}
}
return query;
}
//FILTER DATA
//Returns the filtered data
filterData = (dataset, query) => {
const filteredData = dataset.filter((item) => {
for (let key in query) {
if (item[key] === undefined || !query[key].includes(item[key])) {
return false;
}
}
return true;
});
return filteredData;
};
//FETCH JSON
const dataset = [{
"GEN": "2"
}, {
"GEN": "1"
}, {
"GEN": "1"
}, {
"GEN": "2"
},
{
"GEN": "2"
}, {
"GEN": "2"
}, {
"GEN": "2"
}, {
"GEN": "1"
}
]
//BUILD THE FILTER
const query = buildFilter(filter);
const result = filterData(dataset, query);
console.log(result)
return result
}
<script src="https://code.highcharts.com/highcharts.js"></script>
<div id="gender"></div>
does anyone can explain me how to handle the following?
I have two functions that filter data and than I build a chart with Hichart
Each time a user click for example a slice of a pie chart an event is fired and an object is populated.
That object allows me to filter the dataset and redraw the chart
The last thing I'm missing is about to update the filtering functions based on the object to be populated
first I'll do this
async function getData() {
buildFilter = (filter) => {
let query = {};
for (let keys in filter) {
if (filter[keys].constructor === Array && filter[keys].length > 0) {
query[keys] = filter[keys];
}
}
return query;
}
then
filterData = (data, query) => {
const filteredData = data.filter( (item) => {
for (let key in query) {
if (item[key] === undefined || !query[key].includes(item[key])) {
return false;
}
}
return true;
});
return filteredData;
};
const query = buildFilter(filter);
const result = filterData(data, query);
my object is
let filter = {}
when a user click the slice myobject become for example
let filter = {
gen: "1"
}
Take a look at this StackBlitz project.
In getData(), I simplified your filter to this one:
return data.filter(item => {
for (const property of Object.keys(filter)) {
if (item[property] !== filter[property]) {
return false;
}
}
return true;
});
and when a slice is clicked, I call genderPie() again, after updating the filter.
You might want to separate the data request from the filtering, so that the data is downloaded only once, not every time a filter is changed.

Two sorting / filtering methods used at the same time Javascript

I have two methods - sort and more_than, Here is my JS:
const originalData = [{
"id": 2,
"title": "ASD",
"number": 50,
"src": "https://cloudfour.com/examples/img-currentsrc/images/kitten-small.png"
},
{
"id": 1,
"title": "FGH",
"number": 150,
"src": "https://cloudfour.com/examples/img-currentsrc/images/kitten-small.png"
}
]
const data = [...originalData]
const info_container = document.querySelector('.info-container')
const sort = () => {
const newData = data.sort((a, b) => parseFloat(a.id) - parseFloat(b.id))
fetchData(newData)
}
const more_than = (e) => {
if (e) {
const newData = data.filter((a) => {
return parseFloat(a.number) > parseFloat(e)
})
fetchData(newData)
} else return
}
const clear_filters = () => {
const radio = document.querySelector('input[name="sort"]')
radio.checked = false
fetchData(originalData)
}
const fetchData = (data) => {
info_container.innerHTML = "";
data.forEach((item, index) => {
const img = document.createElement("img");
const title = document.createElement('h3')
const node = document.createTextNode(item.src);
const node_title = document.createTextNode(item.title)
title.appendChild(node_title)
img.src = item.src
info_container.appendChild(title)
info_container.appendChild(img);
})
}
window.onload = function() {
fetchData(originalData)
}
<div><input type="radio" name="sort" onclick="sort()" />sort</div>
<div>More <input min="1" max="1000" oninput="more_than(value)" type="number" name="pages" /> than</div>
<div><button onclick="clear_filters()">Clear</button></div>
<div class="info-container">
</div>
plunker: http://plnkr.co/edit/HFuL37jL9ZHbvI2lib6t?p=preview
I want to use them in the same time. Now my sort function disappears after more_than and vice versa. How to fix that?
Thanks for answers in advance!
You want to have a kind of "meta" instance which allows you to keep the application state intact. That means, you need a way to keep the user selection stored inside variables. In the example below, Store acts as a repository for your data which also knows how many "pages" should be displayed as well as if the data should be sorted by .id. Calling .getItems() returns you a list of sorted/unsorted values, optionally filtered by "pages". Please note that Store doesn't alter the original data. It instead returns a new copy of the original data every time you call .getItems().
const originalData = [{
"id": 2,
"title": "ASD",
"number": 50,
"src": "https://cloudfour.com/examples/img-currentsrc/images/kitten-small.png"
},
{
"id": 1,
"title": "FGH",
"number": 150,
"src": "https://cloudfour.com/examples/img-currentsrc/images/kitten-small.png"
}
]
const Store = data => {
let __sorted = false;
let __pages = 0;
const _Store = {
sorted() {
__sorted = true;
return _Store;
},
unsorted() {
__sorted = false;
return _Store;
},
setPages(num) {
if (typeof num === 'number' && !isNaN(num)) {
__pages = Math.abs(num); // ensure we have a positive number
}
return _Store;
},
unsetPages() {
__pages = 0; // revert back to default
return _Store;
},
getItems() {
let items = [...data];
if (__sorted) {
return __pages <= 0 ? items.sort((a, b) => Number(a.id) - Number(b.id)) :
/* else */ items.filter(a => Number(a.number) >= __pages).
sort((a, b) => Number(a.id) - Number(b.id))
}
return __pages <= 0 ? items :
/* else */ items.filter(a => Number(a.number) >= __pages);
}
};
return _Store;
};
const dataStore = Store(originalData);
const $checkSort = document.querySelector('input[name="sort"]');
const $inputPages = document.querySelector('input[name="pages"]');
const $resetFilters = document.querySelector('#filter-reset');
const $output = document.querySelector('.info-container')
function onCheckSorted (event) {
if (event.target.checked) {
dataStore.sorted();
} else {
dataStore.unsorted();
}
show();
}
function onChangePages (event) {
let v = Number(event.target.value.trim());
if (v && !isNaN(v)) {
dataStore.setPages(v);
} else {
dataStore.unsetPages();
}
show();
}
function onClickReset () {
$checkSort.checked = null; // update UI
$inputPages.value = null; // update UI
dataStore.unsetPages().unsorted();
show();
}
function show () {
$output.innerHTML = "";
dataStore.getItems().forEach((item, index) => {
const img = document.createElement("img");
const title = document.createElement('h3')
const node = document.createTextNode(item.src);
const node_title = document.createTextNode(item.title)
title.appendChild(node_title)
img.src = item.src
$output.appendChild(title)
$output.appendChild(img);
});
}
$checkSort.addEventListener('change', onCheckSorted);
$inputPages.addEventListener('input', onChangePages);
$resetFilters.addEventListener('click', onClickReset);
// kick off
show();
<div><input type="checkbox" name="sort" />sort</div>
<div>More <input min="1" max="1000" type="number" name="pages" /> than</div>
<div><button id="filter-reset">Clear</button></div>
<div class="info-container">
</div>

Categories