So I have an array of names:
const names = ['student1', 'student2', 'student3']
and I have an array of attendance objects:
const attendance = [
{student1: ['On Time', 'Late']},
{student2: ['Late', 'Late']},
{student3: ['On Time', 'Excused']},
]
I wanted to find the student object in the attendance array based off the names from the names array.
So currently I have:
names.forEach(person => {
function attendanceData(p) {
return Object.keys(attendance).toString() == p.toString()
}
console.log(attendance.find(attendanceData(person)))
})
However, this gives me an error saying:
Uncaught (in promise) TypeError: false is not a function
The next stack says "at Array.find()"
I'm wondering how I'm not using this correctly and if there was a better way to do this, what should I do?
I believe this is what you want. Your data is structured a little strangely though, so I would like to know on a grander scale what you want this code to do.
const findStudentAttendance = (att, studentName) => {
return att.find(obj => Object.keys(obj)[0] === studentName)
}
names.forEach(name => {
console.log(
findStudentAttendance(attendance, name)
)
}) /* =>
{ student1: [ 'On Time', 'Late' ] }
{ student2: [ 'Late', 'Late' ] }
{ student3: [ 'On Time', 'Excused' ] }
*/
try it
let result = attendance.filter(obj => names.includes(Object.keys(obj)[0]))
console.log(result)
Use array's method "filter" for getting items. You need get keys of object and take first key. And then you'll can check presence in names array, via array's method "includes"
more information about this methods here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Filter
Related
I have an array of objects containing world countries with some additional information e.g.
countries = [
{
flag: 'assets/flags/angola.svg',
code: 'AGO',
name: 'Angola',
regions: [{
name: 'Luanda'
}]
},
{
flag: 'assets/flags/albania.svg',
code: 'ALB',
name: 'Albania',
regions: [{
name: 'Korça'
}, {
name: 'Tirana'
}, {
name: 'Gjirokastër'
}]
}...
I want to extract three of my favorite countries into a new array while removing them from the original array so I end up with two arrays one for my favorite countries and one for the rest of the countries.
I managed to achieve this the following way:
public createCountriesList(allCountries: Country[]) {
let topCountries: Country[] = [];
let restOfCountries: Country[];
allCountries.forEach((element) => {
switch (element.code) {
case 'HRV':
topCountries.push(element);
break;
case 'AT':
topCountries.push(element);
break;
case 'GER':
topCountries.push(element);
break;
}
});
restOfCountries = allCountries.filter((c) => {
return !topCountries.includes(c);
});}
It works, but I was wondering if there is a more elegant way to do this?
Everything seems fine according to me...
Obviously you need two arrays one for the extracted ones and second for rest of countries.
One thing we can work on is the switch case.
Instead of switch case you can use .includes function.
Store the name of countries you want to extract in an array.
const arr = ['HRV','AT','GR']
now you can do,
if(arr.includes(element.code)){
//push into new array
} else{
//push into another
}
One more thing you can do is save restOfTheCountries using .filter function.
Just return true for the countries which fails your above if case.
You can just use regular filter to split the array:
const isTop = ({code}) => ['HRV','AT','GR'].includes(code);
const topCountries = allCountries.filter(isTop);
const restOfCountries = allCountries.filter((contry) => !isTop(contry));
Another way, you can add a property that shows whether this country is top or not, and filter by this key
const withTop = countries.map((e) => ({...e, top: ['AGO','AT','GR'].includes(e.code)}));
// {
// code: "AGO"
// flag: "assets/flags/angola.svg"
// name: "Angola"
// regions: [{…}]
// top: true
// }
I would probably create a separate generic function for splitting array based on the criteria (using ts since you are)
const splitArray = <T>(array: Array<T>, matchFunction: (el: T) => boolean) => {
const matching: T[] = [], nonMatching: T[] = []
array.forEach(el => matchFunction(el) ? matching.push(el) : nonMatching.push(el))
return [matching, nonMatching]
}
then you can call it with the array and a function
const [topCountries, restOfCountries] = splitArray(countries, c => ["HRV", "AT", "GER"].includes(c.code))
that would be a bit more readable. a more elegant solution is to extend Array with that functionality (Array.prototype.split) then using countries.split(c => ["HRV", "AT", "GER"].includes(c.code))
All suggestions so far seem valid, I ended up using #Alexandr Belan answer as it was the most straightforward to implement in my case, I don't know why I used switch case instead of filter like for the topCountries 🤷♂️. Final code (added alphabetical sorting of the countries as well)
const suggestedCountriesList = ['Brazil', 'France', 'Germany'];
const suggested = ({ name }) => suggestedCountriesList.includes(name);
const suggestedCountries = allCountries.filter(suggested);
const otherCountries = allCountries.filter((country) => !suggested(country));
// sort alphabetically
otherCountries.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
suggestedCountries.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
return [suggestedCountries, otherCountries];
I'm trying to get my head around map functions currently without much success.
I currently have a list of objects and am trying to remap to a new object using the map function to return a map indexed by the created property with a list of wooids.
Having written my map function, I only seem to be returning the last time.
const variants = [
{
created: '2022-03-06',
hashname: 'c78ba80402290724609a5e98c369c90984494152',
hashobject: '80864e6329d5e305a512ace872ad7d56a3f41095',
hashparent: '19c5d50ddddeb7c9a92469df78c47d9d611f1599',
action: 'added',
wooid: 7288
},
{
created: '2022-03-06',
hashname: 'c78ba80402290724609a5e98c369c90984494152',
hashobject: '80864e6329d5e305a512ace872ad7d56a3f41095',
hashparent: '19c5d50ddddeb7c9a92469df78c47d9d611f1599',
action: 'added',
wooid: 7289
}
]
const res = Object.fromEntries(variants.map(k => [k.created, [k.wooid]]))
console.log(res)
Current output
{ '2022-03-06': [ 7289 ] }
Desired output:
{ '2022-03-06': [ 7289, 7288 ] }
Any help would be greatly appreciated as I'm unsure how to dynamically populate the array to include all other entires (6 in total).
Use Array.prototype.reduce() to build up your object. Check if you already have a key for each created value and if not, create an empty array. Then append the wooid value
const variants = [{"created":"2022-03-06","hashname":"c78ba80402290724609a5e98c369c90984494152","hashobject":"80864e6329d5e305a512ace872ad7d56a3f41095","hashparent":"19c5d50ddddeb7c9a92469df78c47d9d611f1599","action":"added","wooid":7288},{"created":"2022-03-06","hashname":"c78ba80402290724609a5e98c369c90984494152","hashobject":"80864e6329d5e305a512ace872ad7d56a3f41095","hashparent":"19c5d50ddddeb7c9a92469df78c47d9d611f1599","action":"added","wooid":7289}]
const res = variants.reduce((acc, { created, wooid }) => ({
...acc,
[ created ]: [
...acc[created] ?? [], // init to an empty array if unset
wooid
]
}), {})
console.log(res)
This will collect wooid values by created in the order they appear in the original data.
I have this constant packages that includes an array of object_tags
const packages = [{
"id":"11",
"object_tags":[
{
"id":"400",
"tag":{
"id":"30",
"name":"Sample Tag"
},
}
],
},
{
"id":"12",
"object_tags":[
{
"id":"401",
"tag":{
"id":"31",
"name":"Lost"
},
}
],
}]
and I want to filter the packages that only have the tag with the name Lost. So I tried .filter()
this.taggedOrders = packages.filter(item => item.object_tags[0].tag.name === 'Lost');
But I am getting an error:
Uncaught TypeError: Cannot read property 'tag' of undefined
So I tried doing nested .filter()
this.taggedOrders = packages.filter(item => item.object_tags.filter(x => x.tag.name === 'Lost'));
but now it just returns the whole array, nothing filtered.
What am I missing?
I got it. I used .some()
this.taggedOrders = packages.filter(item => item.object_tags.some(x => x.tag.name === 'Lost'));
you are almost there, with nested filter, you need to get to the first filter condition as well.. in this case you can use condition where the length > 0. ex:
this.taggedOrders = packages.filter(p => p.object_tags.filter(t => t.tag.name === 'Lost').length > 0);
I have a huge chunk of data directly from the database, that data is stored in a JS object : {}, however I'm unable to use built-in JS's .filter function.
Avoid arrow functions as I'm using Vue.JS.
Example of JS Object:
0:
brand: "Brand A"
compatibilitySetIds:Array(3)
createdAt:"2018-08-13T09:07:50.772Z"
deletedAt:null
id:"e7915261-677d-4527-90c6-09b5170afca8"
model:"Model-1"
series:null
type:"A"
updatedAt:"2018-08-13T09:07:50.772Z"
1:
brand: "Brand B"
compatibilitySetIds:Array(3)
createdAt:"2018-08-13T09:07:50.772Z"
deletedAt:null
id:"e7915261-677d-4527-90c6-09b5170afca8"
model:"Model-51"
series:"S02"
type:"B"
updatedAt:"2018-08-13T09:07:50.772Z"
I need to be able to:
Filter by Model
Preferably filter also by Brand/Series/Type
Be able to sort asc/desc in all fields
What I currently have (works)
computed: {
filteredEquipment: function() {
if (this.search.selected === '' || !this.search.selected) {
return this.equipmentItems;
} else {
const model = this.search.selected;
console.log(model);
const filtered = this.equipmentItems.filter(function(item) {
return item.model === model;
});
return filtered;
}
},
},
PS.
Using Vue-JS along with TypeScript.
PPS.
I must filter on the client side, not on the server side.
I'm assuming that your data items are wrapped inside an array, since you have object keys "0" and "1", which seem to be indexes to me. Also you use filter yourself in your code.
You have a couple of choices here to use .filter().
So you can choose which best suites your coding style.
const data = [
{
brand: "Brand A",
compatibilitySetIds: [],
createdAt:"2018-08-13T09:07:50.772Z",
deletedAt:null,
id:"e7915261-677d-4527-90c6-09b5170afca8",
model:"Model-1",
series:null,
type:"A",
updatedAt:"2018-08-13T09:07:50.772Z"
},
{
brand: "Brand B",
compatibilitySetIds: [],
createdAt:"2018-08-13T09:07:50.772Z",
deletedAt:null,
id:"e7915261-677d-4527-90c6-09b5170afca8",
model:"Model-51",
series:"S02",
type:"B",
updatedAt:"2018-08-13T09:07:50.772Z"
}
];
//Specific one liner to filter all Model-1 models.
const AllModel_1Items = data.filter( item => item.model === 'Model-1' );
console.log( AllModel_1Items );
// Generic function with 3 parameters:
const filterProperty = ( source, property, value ) => source.filter( item => item[ property ] === value );
const allTypeA = filterProperty( data, 'type', 'A' );
console.log( allTypeA );
// Generic function usable with .filter()
const byProperty = ( property, value ) => item => item[ property ] === value;
const allBrandA = data.filter( byProperty( 'brand', 'Brand A' ));
console.log( allBrandA );
These functions can filter any property. Sorting asc/desc is just .reverse() the array after .sort().
If for some reason arrows can't be used or filter cannot be used, the same structure can be applied to a simple for loop to mimic .filter().
But since you use both .filter() and arrows in your own code, I'll assume that you can use them.
Object.keys(dbObject) this will convert into array. Then you should be able to use just all the other filter and etc. methods
You can use a loop for read array, and add conditions for filter your data, for example:
let dataFiltered = [];
let data = [
{
brand: "Brand A",
compatibilitySetIds:Array(3),
createdAt:"2018-08-13T09:07:50.772Z",
deletedAt:null,
id:"e7915261-677d-4527-90c6-09b5170afca8",
model:"Model-1",
series:null,
type:"A",
updatedAt:"2018-08-13T09:07:50.772Z",
},
{
brand: "Brand B",
compatibilitySetIds:Array(3),
createdAt:"2018-08-13T09:07:50.772Z",
deletedAt:null,
id:"e7915261-677d-4527-90c6-09b5170afca8",
model:"Model-51",
series:"S02",
type:"B",
updatedAt:"2018-08-13T09:07:50.772Z",
}];
//Use a loop for read array
data.forEach(function(item){
if(item.model === 'Model-1'){
//Add conditions after push item to dataFilter
dataFiltered.push(item);
}
})
console.log(dataFiltered);//In dataFilter is data filtered
I do get my data from mongoDB with
Content.find({}).fetch()
So there is an array with 1000 documents. Every document has a category field.
const data = [
{
title: 'Article 1',
category: 'cars'
},
{
title: 'Article 2',
category: 'vegetables'
}
]
Now I would like to display a list with the documents organized in categories (Getting an own list for each category).
I think the best way I should go is to create a map. But I do not know how to do that.
This is what I am doing:
First I've hardcoded the categories (knowing this is the worst thing I can do)
Then...
categories.map(category => {
return (<List data={data.filter(d => d.category === category)} />)
}
With this I will go through 30 categories and do a filter on my data 30 times. I think it would be smarter to create a kind of sorted dataset once at the beginning.
If you'd prefer using lodash, then you can sort your data set using sortBy
_.sortBy(data, ['category', 'title']);
data.map(doc => ( console.log(doc); ));
This will sort your data ascending first by category and then by title.
OR if you want to break your items into arrays based on category use groupBy:
_.groupBy(data, 'category');
_.each(data, itemsByCategory => {
return <List data={itemsByCategory} />;
});
Since aggregating data on mongodb is not an option, I would create a map on the client side by looping once over the data, like:
var titlesByCategory = {};
for (let value of data) {
if (titlesByCategory[data.category]) {
titlesByCategory[value.category].push(value.title);
}
else {
titlesByCategory[value.category] = [value.title];
}
}
You can then access the array of titles of a given a category just using the category as a key; for instance titlesByCategory[vegetables] will return the array of vegetables over which you can do loop/map or whatever you need.
using underscoreJS
_.groupBy(data, 'length');