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
Related
I'm working with the outputs of an Intranet I don't control.
I have this string:
let template = 'LAWYER=|FIRM=|SUIT_DESCRIPTION=|DEF_COMMENT=|PLF_COMMENT=|';
It goes on longer, but that's the pattern.
Now there's another similar string, but with data assigned, as in this example:
let current= 'FIRM=Smith and Wesson LLP|SUIT_DESCRIPTION=It\'s a royal mess|PLF_COMMENT=some freeform text|LAWYER=Bob Smith';
Now, notice that not every element in template is necessarily represented in current, and the order may be different (if the latter fact is a big deal, I can ensure the order is the same).
What I'm trying to do, is take every element that is in current, and populate the matching element in template, if it exists. (or, alternatively and potentially preferred, insert every non-matching element in template into current, but ideally in the same order as template).
Using the date above, the result I'm looking for is:
result = 'LAWYER=Bob Smith|FIRM=Smith and Wesson LLP|SUIT_DESCRIPTION=It\'s a royal mess|DEF_COMMENT=|PLF_COMMENT=some freeform text|';
I'm not very accomplished with JavaScript :(
I tried various things in JSFiddle using split() and match() but I just made a mess of it.
// Convert the template to an array of keys
const getKeys = str => str.split('|').map(entry => entry.split('=')[0]);
// Convert the data to an object
const toObj = str => Object.fromEntries(str.split('|').map(entry => entry.split('=')));
// Reconcile the data with the template
const compile = (templateStr, dataStr) => {
const keys = getKeys(templateStr);
const data = toObj(dataStr);
return keys.reduce((results, key) => {
if(key) results.push([key, data[key] ?? '']);
return results;
}, []);
};
// Convert the results back into a string
const toString = data => data.map(entry => entry.join('=')).join('|') + '|';
// And then a test
let template = 'LAWYER=|FIRM=|SUIT_DESCRIPTION=|DEF_COMMENT=|PLF_COMMENT=|';
let current = 'FIRM=Smith and Wesson LLP|SUIT_DESCRIPTION=It\'s a royal mess|PLF_COMMENT=some freeform text|LAWYER=Bob Smith';
console.log(toString(compile(template, current)));
I have a code that converts user input to an array then does a fetch request. The rest of the code only works when the array has a quantity (as a string) and a unit (as a string). So for example, it works when I type in "1 whole apple" or "1 oz chicken thigh" but it breaks when it's just "apple". How can I set up a checkpoint to add "1" and "whole" to the beginning of the array when quantity and unit are missing?
const [input, setInput] = useState("")
const foodName = []
const searchArray = []
// This part below separates user input by space then forms an array
const inputArray = input.split(/(\s+)/).filter(e => String(e).trim())
// This part below forms a new array with only the quantity and the unit, or nested arrays if the user inputs more than one item, which obviously breaks if there is no quantity.
const array = inputArray.reduce(
(arrays, value) => (
isFinite(value)
? arrays.push([value])
: arrays[arrays.length - 1].push(value),
arrays
),
[]
)
// This part below combines food name to a single element if it has more than one word i.e. apple pie.
for (var i = 0; i < array.length; i++) {
const foodName = array[i].splice(2).join(' ')
foodArray.push(foodName)
const wholeArray = array[i].concat(foodArray[i])
searchArray.push(wholeArray)
}
Making the fetch request etc.
Thanks in advance!
So I ended up adding an error message since I could not find a solution to this.
if (!isFinite(inputArray[0])) {
setErrorMessage('Be sure to specify quantity and unit!')
return
}
const array = inputArray.reduce(
(arrays, value) => (
isFinite(value)
? arrays.push([value])
: arrays[arrays.length - 1].push(value),
arrays
),
[]
)
Right now my code is looking for the words 'Cheese' or 'Bread' within a specific webpage, and if it finds either word it should display an alert. However, it only displays the alert if the first word is found (cheese). Any suggestions on how to fix it so that it will successfully look for more than one word?
var array = Array.from(document.getElementsByClassName('wide-content-host'))
.find(el => el.innerText.includes('Cheese', 'Bread'));
if (array){
alert("Word found!")
}
This is an obvious change, but we could put an OR operator inside of the statement to signify both of them, like so:
let array = Array.from(document.getElementsByClassName('wide-content-host'))
.find(el => el.innerText.includes('Cheese') || el.innerText.includes('Bread'));
if (array) alert('Word found!');
You could also do it a more elegant way, like so:
const conditions = ['Cheese', 'Bread'];
const array = Array.from(document.getElementsByClassName('wide-content-host'));
const results = array.find((el) => conditions.some(nEl => el.innerText.includes(nEl)));
if (results) alert('Word found!');
This one works by grabbing the array from the 'wide-content-host' class name, then looping through that array with another loop that is looping through the values of the conditions array. With all of these loops working together, it will check whether or not the elements include the conditions.
** Edit **
In order to make the methods work without case-sensitivity, you would need to make the search cases lowercase e.g. 'cheese' and 'bread', and you would need to make the strings that you are searching through completely lowercase also.
Here are the examples for case-insensitivity:
let array = Array.from(document.getElementsByClassName('wide-content-host'))
.find(el => el.innerText.toLowerCase().includes('Cheese') || el.innerText.toLowerCase().includes('Bread'));
if (array) alert('Word found!');
or
const conditions = ['cheese', 'bread'];
const array = Array.from(document.getElementsByClassName('wide-content-host'));
const results = array.find((el) => conditions.some(nEl => el.innerText.toLowerCase().includes(nEl)));
if (results) alert('Word found!');
This can be done with regular expressions
let elem = document.querySelector("section");
let entries = elem.innerHTML.match(/(cheese)|(bread)/gi);
if (entries.length > 0) {
alert(`Words were found: ${entries}`);
}
<section>
<p>Cheese Bread</p>
<p>cheeSE BREAD</p>
</section>
const gotCitiesCSV =
"King's Landing,Braavos,Volantis,Old Valyria,Free Cities,Qarth,Meereen";
let returnOnly = gotCitiesCSV => Array.from(gotCitiesCSV).filter(letter =>
'aa','ee','ii','oo','uu'.includes(letter)).length;
let callElement = document.createElement("div");
callElement.textContent = JSON.stringify(returnOnly)
document.getElementById('kata23').appendChild(callElement)
return returnOnly;
}
When i try to run my code nothing is returning. So not sure if i am not calling the double vowels correctly. Trying to return the items that has double vowels and as well return as an array instead of a string.
Looks like there is a few things going on in that example, but i took a stab at creating a working solution for ya with explanations about whats going on
const gotCitiesCSV =
"King's Landing,Braavos,Volantis,Old Valyria,Free Cities,Qarth,Meereen";
// 1. Array.from() wont split the list of cities into an array
const gotCities = gotCitiesCSV.split(',');
// 2. `returnOnly` is a function, not a result. it looks like you want it to be the result;
const doubleVowels = ['aa','ee','ii','oo','uu'];
const doubleVowelCities = gotCities.filter(
// filter in the cities which
cityName => doubleVowels.some(
// contain some double vowel (i.e., atleast one)
doubleVowel => cityName.includes(doubleVowel),
),
)
which returns
> console.log(doubleVowelCities);
[ 'Braavos', 'Free Cities', 'Meereen' ]
Seems you were nearly there but your logic was a bit wrong. You need to use split to break cities from comma and check each individual one against the vowels you have.
Need to use includes on each string found in filter function so that it can checked if they vowels.
Live Demo:
const gotCitiesCSV = "King's Landing,Braavos,Volantis,Old Valyria,Free Cities,Qarth,Meereen";
let splitStr = gotCitiesCSV.split(',') //split in commas
splitStr.filter(function(x) {
if (x.includes('aa') || x.includes("ee") || x.includes('ii') || x.includes('oo') || x.includes('uu')) {
let callElement = document.createElement("div");
callElement.textContent = JSON.stringify(x)
document.getElementById('kata23').appendChild(callElement)
}
})
<div id="kata23"></div>
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).