I'm looking for a way to identify the dist.xml files that are in the top most directory.
Example, I have this list of directory listing,
/opt/pictures/dist.xml
/opt/docs_old/dist.xml
/opt/public/dist.xml
/opt/documents/server/dist.xml
/opt/documents/dist.xml
/opt/documents/web/dist.xml
/opt/documents/class/dist.xml
/opt/documents/lessons/1/dist.xml
/opt/documents/lessons/2/dist.xml
/opt/documents/lessons/3/dist.xml
/opt/documents/lessons/4/dist.xml
/opt/documents/lessons/5/dist.xml
/opt/music/service/day/dist.xml
/opt/music/service/week/dist.xml
/opt/music/service/month/dist.xml
/opt/music/service/month/1/dist.xml
/opt/music/service/month/2/dist.xml
and I'm looking to have this output instead,
/opt/pictures/dist.xml
/opt/docs_old/dist.xml
/opt/public/dist.xml
/opt/documents/dist.xml
/opt/music/service/day/dist.xml
/opt/music/service/week/dist.xml
/opt/music/service/month/dist.xml
I'm trying to achieve this in JS. I thought a simple sort would work.
You could sort thes strings by the count of slashes and then by character and filter the array by having a look to seen pathes.
var data = ['/opt/pictures/dist.xml', '/opt/docs_old/dist.xml', '/opt/public/dist.xml', '/opt/documents/server/dist.xml', '/opt/documents/dist.xml', '/opt/documents/web/dist.xml', '/opt/documents/class/dist.xml', '/opt/documents/lessons/1/dist.xml', '/opt/documents/lessons/2/dist.xml', '/opt/documents/lessons/3/dist.xml', '/opt/documents/lessons/4/dist.xml', '/opt/documents/lessons/5/dist.xml', '/opt/music/service/day/dist.xml', '/opt/music/service/week/dist.xml', '/opt/music/service/month/dist.xml', '/opt/music/service/month/1/dist.xml', '/opt/music/service/month/2/dist.xml'],
result = data
.sort((a, b) =>
a.replace(/[^\/]+/g, '').length - b.replace(/[^\/]+/g, '').length ||
a > b || -(a < b)
)
.filter(
(seen => s =>
(parts =>
!parts.some((_, i, p) => seen.has(p.slice(0, i + 1).join('/'))) &&
seen.add(parts.join('/'))
)
(s.split('/').slice(0, -1))
)
(new Set)
);
console.log(result);
No sorting needed. Put all the paths in a Set, then subsequently filter the array for those paths for which no ancestor exists in the set.
var paths = ['/opt/pictures/dist.xml', '/opt/docs_old/dist.xml', '/opt/public/dist.xml', '/opt/documents/server/dist.xml', '/opt/documents/dist.xml', '/opt/documents/web/dist.xml', '/opt/documents/class/dist.xml', '/opt/documents/lessons/1/dist.xml', '/opt/documents/lessons/2/dist.xml', '/opt/documents/lessons/3/dist.xml', '/opt/documents/lessons/4/dist.xml', '/opt/documents/lessons/5/dist.xml', '/opt/music/service/day/dist.xml', '/opt/music/service/week/dist.xml', '/opt/music/service/month/dist.xml', '/opt/music/service/month/1/dist.xml', '/opt/music/service/month/2/dist.xml'];
const all = new Set(paths);
const result = paths.filter(path => {
const parts = path.split("/");
const filename = parts.pop();
return !Array.from({length: parts.length}, (_, i) => i).some(i =>
all.has(parts.slice(0, i).concat([filename]).join("/"))
);
});
console.log(result);
Related
this code displays the points list randomly ,I want to display points list from the highest to the lowest.
if (command == prefix + 'points') {
if (!message.guild.member(client.user).hasPermission('EMBED_LINKS'))
return message.channel.send(':no_entry: | I dont have Embed Links permission.');
if (!args[1]) {
if (!points)
return message.channel.send(embed);
var members = Object.values(points, null, 5);
var memb = members.filter(m => m.points >= 1);
if (memb.length == 0)
return message.channel.send(embed);
var x = 1;
let pointsTop = new Discord.MessageEmbed()
.setAuthor('Points:')
.setColor('#79758F')
.setDescription(
memb.sort(
(second, first) => first.points > second.points
)
.slice(0, 10)
.map(m => `<#${m.id}> \`${m.points}\``).join('\n')
)
message.channel.send({ embed: pointsTop });
You can use - in Array#sort()
.setDescription(memb.sort((a, b) => a.points - b.points).slice(0, 10).map(m => `${m} \`${m.points}\``).join('\n');
Sort functions should return numbers, not booleans
This is the only part of your code sample that matters:
memb.sort(
(second, first) => first.points > second.points
)
Your custom sort function returns a boolean. That's not how Array.prototype.sort works.
return a negative number if the first argument should sort before the second argument
return a positive number if the second argument should sort before the first argument
return 0 if neither belongs before the other
You probably want this:
memb.sort(
(first, second) => second.points - first.points
)
I have my previous question in this link my question
I asked to push all values into an array and show to the HTML. They responded well but it showing only one value(zip1) into an array and get them to HTML.
So i want to get that all values like zip1,zip2, distance, weight based on the group number.
I tried but answer not came
my code altered from previous answer.
const array = [[{"loc":{}},{"distance":6.4},{"zip1":"06120"},{"zip2":"06095"},{"group":1},{"weight":1119}],[{"loc":{}},{"distance":6.41},{"zip1":"06095"},{"zip2":"06120"},{"group":2},{"weight":41976}],[{"loc":{}},{"distance":6.41},{"zip1":"06095"},{"zip2":"06120"},{"group":1},{"weight":41976}]];
const merged = array.map((r, a) =>{
const { group } = a.find(n => n.group)
const { zip1 } = a.find(n => n.zip1)
r[group] = r[group] || []
r[group].push({Zip1:zip1})
const { zip2 } = a.find(n => n.zip2)
r[group].push({Zip2:zip2})
const { weight } = a.find(n => n.weight)
r[group].push({weight:weight})
const { distance } = a.find(n => n.distance)
r[group].push({distance:distance})
return r;
},{})
const output = document.getElementById('output');
Object.entries(merged).forEach(([group, zips]) => {
const h1 = document.createElement('h1');
h1.innerHTML = "group " + group
const span = document.createElement('span');
span.innerHTML = `Zip1 - ${zips.zip1},${zips.zip2},${zips.weight},${zips.distance} (in group - ${group})`;
output.appendChild(h1)
output.appendChild(span)
})
My expected output(but I need to show this in google map infowindow.I just showing example content)
Methodology
Convert your 2D array into a 1D array, so instead of having arrays as inner items you will have objects. This is done through the arrToObj function
Convert your zip values from string to array. This is done to facilitate their _concatenation in the future. Done through the zipToArr function
Group your array of objects under one object. In order to do that we promote the group key and concatenate zip1/zip2 with other objects from the same group. Refer to the grouper function
Get the grouped objects using Object.values on the previous aggregate. We already have the group key in them so we don't need the parent key anymore
Format your values into HTML elements based on their respective keys. This will facilitate generating the HTML in the end since we'll have the elements ready. Done with html and format functions
Render your HTML by iterating on the previously generated array. In each iteration create a container div that will hold the group. The container will help styling its first element group
Implementation
const array = [[{"loc":{}},{"distance":6.4},{"zip1":"06120"},{"zip2":"06095"},{"group":1},{"weight":1119}],[{"loc":{}},{"distance":6.41},{"zip1":"06095"},{"zip2":"06120"},{"group":2},{"weight":41976}],[{"loc":{}},{"distance":6.41},{"zip1":"06095"},{"zip2":"06120"},{"group":1},{"weight":41976}]];
// Data processing functions
const arrToObj = arr => arr.reduce((a, c) => ({ ...a, ...c}), {});
const zipToArr = x => ({...x, zip1: [x.zip1], zip2: [x.zip2]});
const grouper = (a, c) => {
delete c.loc;
delete c.distance;
if (a[c.group]) {
a[c.group].zip1.push(...c.zip1);
a[c.group].zip2.push(...c.zip2);
return a;
} else {
return {...a, [c.group]: c}
}
};
// HTML utilities
const html = (k, v) => {
const it = document.createElement('p');
it.innerHTML = `${k} ${v}`;
return it;
}
const format = g => Object.keys(g).sort().reduce((a, c) => ({...a, [c]: html(c, g[c])}), {});
// Actual processing
const data = array.map(arrToObj).map(zipToArr).reduce(grouper, {});
const dataWithHTML = Object.values(data).map(format);
// Rendering
const display = document.getElementById('display');
dataWithHTML.forEach(it => {
const container = document.createElement('div');
Object.values(it).forEach(v => container.appendChild(v));
display.appendChild(container);
});
p:first-of-type {
font-size: 36px;
font-weight: bold;
margin: 0;
}
p {
text-transform: capitalize;
}
<div id="display"></div>
I can get my tags. i.e., Pitcher, Pitcher, Catcher, Batter
I can get the unique ones. i.e., Pitcher, Catcher, Batter
How do I get?
Pitcher - 2
Batter - 1
Catcher - 1
const tags = attendance.map((entry) => entry.get('Tags')) // get tags
const uniqueTags = tags.filter((item, index) => tags.indexOf(item) === index) // get unique tags
Assuming tags ends up being ["Pitcher","Pitcher","Catcher","Batter"];
const tags = ["Pitcher","Pitcher","Catcher","Batter"];
const result = Object.entries(tags.reduce((r,t)=>(r[t]=(r[t]||0)+1,r),{})).map(([k,v])=>`<li key="${k}">${k} - ${v}</li>`).join('\n');
console.log(result);
Of course, as you can't see the exact content of tags the way you are using it, it may well be that tags is more like
const tags = ["","Pitcher","","Pitcher","","Catcher","","Batter"];
const result = Object.entries(tags.reduce((r,t)=>(r[t]=(r[t]||0)+1,r),{})).map(([k,v])=>`<li key="${k}">${k} - ${v}</li>`).join('\n');
console.log(tags.join('')); // you can't see the empty tags here
console.log(result); // but this is the result
This is why I asked you to post what attendance is, to see what is happening with the data
But, with filter as I suggested ...
const tags = ["","Pitcher","","Pitcher","","Catcher","","Batter"];
const result = Object.entries(tags.filter(t=>t).reduce((r,t)=>(r[t]=(r[t]||0)+1,r),{})).map(([k,v])=>`<li key="${k}">${k} - ${v}</li>`).join('\n');
console.log(tags.join('')); // you can't see the empty tags here
console.log(result); // with filter, everything is good again
If tags is an array of string (tags names), then you can use reduce to derive the desire output.
Example
var tags = ['Pitcher', 'Pitcher', 'Catcher', 'Batter', 'Batter'];
var result = tags.reduce((acc, val) => {
acc[val] = acc[val] || 0;
acc[val] = acc[val] + 1;
return acc;
}, {});
console.log(result);
Hope this will help!
Edit
To convert the final object into an array of string, you can use .map
Object.entries(result).map(([k, v]) => `${k}-${v}`);
Thanks for the heads up Jaromanda X
I'd like to _.filter or _.reject the cities array using the filters array using underscore.
var cities = ['USA/Aberdeen', 'USA/Abilene', 'USA/Akron', 'USA/Albany', 'USA/Albuquerque', 'China/Guangzhou', 'China/Fuzhou', 'China/Beijing', 'China/Baotou', 'China/Hohhot' ... ]
var filters = ['Akron', 'Albuquerque', 'Fuzhou', 'Baotou'];
My progress so far:
var filterList;
if (reject) {
filterList = angular.copy(cities);
_.each(filters, (filter) => {
filterList = _.reject(filterList, (city) => city.indexOf(filter) !== -1);
});
} else {
filterList = [];
_.each(filters, (filter) => {
filterList.push(_.filter(cities, (city) => city.indexOf(filter) !== -1));
});
}
filterList = _.flatten(filterList);
return filterList;
I'd like to DRY this up and use a more functional approach to achieve this if possible?
A somewhat more functional version using Underscore might look like this:
const cities = ['USA/Aberdeen', 'USA/Abilene', 'USA/Akron', 'USA/Albany',
'USA/Albuquerque', 'China/Guangzhou', 'China/Fuzhou',
'China/Beijing', 'China/Baotou', 'China/Hohhot']
const filters = ['Akron', 'Albuquerque', 'Fuzhou', 'Baotou'];
var inList = names => value => _.any(names, name => value.indexOf(name) > -1);
_.filter(cities, inList(filters));
//=> ["USA/Akron", "USA/Albuquerque", "China/Fuzhou", "China/Baotou"]
_.reject(cities, inList(filters));
//=> ["USA/Aberdeen", "USA/Abilene", "USA/Albany",
// "China/Guangzhou", "China/Beijing", "China/Hohhot"]
I'm using vanilla JavaScript here (some() and filter()) but I hope you get the idea:
const isValidCity = city => filters.some(filter => city.indexOf(filter) > -1)
const filteredCities = cities.filter(isValidCity)
Please note that this is a loop over a loop. So the time complexity is O(n * m) here.
In your example all city keys share the same pattern: country + / + city. Your filters are all an exact match to the city part of these names.
If this is a certainty in your data (which it probably isn't...), you could reduce the number of loops your code makes by creating a Map or object that stores each city per filter entry:
Create an object with an entry for each city name
Make the key the part that you want the filter to match
Make the value the original name
Loop through the filters and return the name at each key.
This approach always requires one loop through the data and one loop through the filters. For small array sizes, you won't notice a performance difference. When one of the arrays has length 1, you'll also not notice any differences.
Again, note that this only works if there's a constant relation between your filters and cities.
var cities = ['USA/Aberdeen', 'USA/Abilene', 'USA/Akron', 'USA/Albany', 'USA/Albuquerque', 'China/Guangzhou', 'China/Fuzhou', 'China/Beijing', 'China/Baotou', 'China/Hohhot' ]
var filters = ['Akron', 'Albuquerque', 'Fuzhou', 'Baotou'];
const makeMap = (arr, getKey) => arr.reduce(
(map, x) => Object.assign(map, {
[getKey(x)]: x
}), {}
);
const getProp = obj => k => obj[k];
const getKeys = (obj, keys) => keys.map(getProp(obj));
// Takes the part after the "/"
const cityKey = c => c.match(/\/(.*)/)[1];
const cityMap = makeMap(cities, cityKey);
const results = getKeys(cityMap, filters);
console.log(results);
Since you seem to be using AngularJS, you could utilize the built-in filter functionality. Assuming both the cities and filters array exist on your controller and you're displaying the cities array using ng-repeat, you could have something like this on your controller:
function cityFilter(city) {
var cityName = city.split('/')[1];
if (reject) {
return filters.indexOf(cityName) === -1;
} else {
return filters.indexOf(cityName) > -1;
}
}
And then in your template, you'd do something like this:
<div ng-repeat="city in cities | filter : cityFilter"></div>
Of course you'd have to modify your syntax a bit depending on your code style (for example, whether you use $scope or controllerAs).
Hello I'm trying to figure out if there is an equivalent to the RxJs operator zip in xstream, or at least a way to get the same behaviour. In case anyone needs clarification on the difference the marble diagrams below will show.
zip in rxjs
|---1---2---3-----------5->
|-a------b------c---d----->
"zip"
|-1a----2b------3c-----5d->
whereas 'combineLatest' aka 'combine' in xstream does
|---1---2----------4---5->
|----a---b---c---d------->
"combine"
|-1a----2a-2b-2c-2d-4d-5d>
Any help is appreciated as I'm very new to programming with streams. Thank you in advance!
I also needed a zip operator for xstream. So I created my own from existing operators. It takes an arbitrary number of streams for zipping.
function zip(...streams) {
// Wrap the events on each stream with a label
// so that we can seperate them into buckets later.
const streamsLabeled = streams
.map((stream$, idx) => stream$.map(event => ({label: idx + 1, event: event})));
return (event$) => {
// Wrap the events on each stream with a label
// so that we can seperate them into buckets later.
const eventLabeled$ = event$.map(event => ({label: 0, event: event}));
const labeledStreams = [eventLabeled$, ...streamsLabeled];
// Create the buckets used to store stream events
const buckets = labeledStreams.map((stream, idx) => idx)
.reduce((buckets, label) => ({...buckets, [label]: []}), {});
// Initial value for the fold operation
const accumulator = {buckets, tuple: []};
// Merge all the streams together and accumulate them
return xs.merge(...labeledStreams).fold((acc, event) => {
// Buffer the events into seperate buckets
acc.buckets[event.label].push(event);
// Does the first value of all the buckets have something in it?
// If so, then there is a complete tuple.
const tupleComplete = Object.keys(acc.buckets)
.map(key => acc.buckets[key][0])
.reduce((hadValue, value) => value !== undefined
? true && hadValue
: false && hadValue,
true);
// Save completed tuple and remove it from the buckets
if (tupleComplete) {
acc.tuple = [...Object.keys(acc.buckets).map(key => acc.buckets[key][0].event)];
Object.keys(acc.buckets).map(key => acc.buckets[key].shift());
} else {
// Clear tuple since all columns weren't filled
acc.tuple = [];
}
return {...acc};
}, accumulator)
// Only emit when we have a complete tuple
.filter(buffer => buffer.tuple.length !== 0)
// Just return the complete tuple
.map(buffer => buffer.tuple);
};
}
This can be used with compose.
foo$.compose(zip(bar$)).map(([foo, bar]) => doSomething(foo, bar))