I'm trying to take an array of objects and pick out the data that I only need. For example, below I only want the name, id, and users properties from the originalArray.
I figured out how to do it at the first level of iteration, but how do I do the same for the users array of objects? I only want to include the values in the allowedUserProps array found below.
https://jsfiddle.net/n8zw47cd/
Original Array
var originalArr = [
{
name: 'Obj 1',
id: 0,
something: 'else',
users: [{first_name: 'Joe', last_name: 'Smith'}]
},
{
name: 'Obj 2',
id: 1,
something: 'else',
users: [{first_name: 'Jane', last_name: 'Doe'}]
},
];
Desired Output
[
{
name: 'Obj 1',
id: 0,
users: [{first_name: 'Joe'}]
},
{
name: 'Obj 2',
id: 1,
users: [{first_name: 'Jane'}]
},
];
I'm using Underscore's pick method to return whitelisted values, but how can I change the users array of objects too?
function changeArray(arr) {
var allowedProps = ['name', 'id', 'users'];
var allowedUserProps = ['first_name'];
return _.map(arr, function(item) {
return _.pick(item, allowedProps);
});
}
var transformed = changeArray(originalArr);
Apply another map/pick to the sub-array:
function changeArray(arr) {
var allowedProps = ['name', 'id', 'users'];
var allowedUserProps = ['first_name'];
return _.map(arr, function(item) {
var out = _.pick(item, allowedProps);
out.users = _.map(out.users, function(usersItem) {
return _.pick(usersItem, allowedUserProps);
});
return out;
});
}
Same principle as for the outer array, but once for each sub-array element. This will give you the desired output.
Related
I want to sort the table based on custom values as below how we can write the logic for this from react side?
Order is
no s, no c, ready, pub
as I want to sort the below data
const data = [
{
name: 'Harry',
sort: 'no c',
},
{
name: 'Don',
sort: 'no s',
},
{
name: 'Arc',
sort: 'pub',
},
{
name: 'Park',
sort: 'ready',
},
];
You can create an array of sort value in order that you want to sort data, then use filter function to get those value and push it to output array.
E.g.
let output = []
let order = ['no s','no c','pub','ready']
const data = [ { name: 'Harry', sort: 'no c', }, { name: 'Don', sort: 'no s', }, { name: 'Arc', sort: 'pub', }, { name: 'Park', sort: 'ready', }]
for(const o of order){
output.push(...data.filter(d=>d.sort === o))
}
console.log(output)
const arr = [
{ name: 'Harry', sort: 'no c', },
{ name: 'Don', sort: 'no s', },
{ name: 'Arc', sort: 'pub', },
{ name: 'Park', sort: 'ready', },
];
const order = [ 'no s', 'no c', 'ready', 'pub'];
const orderedArr = [];
order.forEach(element => {
const item = arr.find((obj) => obj.sort === element);
orderedArr.push(item);
});
This solution will sort an array of any length, so long as each element has a sort property.
data.sort((obj1, obj2) => {
const order = ['no s', 'no c', 'ready', 'pub'];
const getOrder = (obj) => order.findIndex(item => item === obj.sort);
return getOrder(obj1) - getOrder(obj2);
}
EDIT:
I made the function cleaner by removing a bulky switch statement and an unnecessary variable.
Note that you need to guarantee each sort value is valid for this solution, otherwise the object will be placed at the front of the array. If you want different behavior for invalid values, you can handle that in the getOrder function if you want invalid ones at the end, or if you want to filter them out altogether, you can just tack on a .filter(obj => order.includes(obj.sort)).
I have a kids object that looks like the following:
const kids = {
name: 'john',
extra: {
city: 'London',
hobbies: [
{
id: 'football',
team: 'ABC',
},
{
id: 'basketball',
team: 'DEF',
},
],
},
};
and i have the following object that contains all sports and extra info for each.
const sports = [
{
name: 'volleyball',
coach: 'tom',
},
{
name: 'waterpolo',
coach: 'jack',
},
{
name: 'swimming',
coach: 'kate',
},
{
name: 'football',
coach: 'sara',
},
];
I want to get the list of all ids in the hobbies array and go through each of the sports items in the sports array, and found, add an extra field to that object available and give a value of true, so the result will look like:
const result = [
{
name: 'volleyball',
coach: 'tom',
},
{
name: 'waterpolo',
coach: 'jack',
},
{
name: 'swimming',
coach: 'kate',
},
{
name: 'football',
coach: 'sara',
available: true
},
];
by the way, here is my attempt:
const result = kids.extra.hobbies.map(a => a.id);
for (var key in sports) {
console.log(sports[key].name);
const foundIndex = result.indexOf(sports[key].name);
if ( foundIndex > -1) {
sports[key].available = true;
}
}
console.log(sports)
but this is too long... i am looking one liner looking code and robust logic.
This can be done many ways; however, an easy was is to divide the problem into two steps:
We can first flatten the kid's hobbies into an array by using the Array.map() function:
const hobbies = kids.extra.hobbies.map(hobby => hobby.id);
Then, we can iterate through the sports array and add an active property to any object which is present in the new hobbies array:
const result = sports.map(sport => {
if (hobbies.indexOf(sport.name) !== -1) {
sport.available = true;
}
return sport;
})
Complete Solution
const kids = {
name: 'john',
extra: {
city: 'London',
hobbies: [{
id: 'football',
team: 'ABC',
},
{
id: 'basketball',
team: 'DEF',
},
],
},
};
const sports = [{
name: 'volleyball',
coach: 'tom',
},
{
name: 'waterpolo',
coach: 'jack',
},
{
name: 'swimming',
coach: 'kate',
},
{
name: 'football',
coach: 'sara',
},
];
const hobbies = kids.extra.hobbies.map(hobby => hobby.id);
const result = sports.map(sport => {
if (hobbies.indexOf(sport.name) !== -1) {
sport.available = true;
}
return sport;
})
console.log(result);
Firstly, I would change my data structures to objects. Any time you have a list of things with unique ids, objects will make your life much easier than arrays. With that in mind, if you must use arrays, you could do the following:
const hobbies = kids.extra.hobbies
sports.forEach(s => s.available = hobbies.some(h => h.id === s.name))
Note that this mutates the original sports object (change to map for new), and also adds false/true instead of just true.
Build an array of the found sports first, then map while checking to see if the sports object's name is in it:
const kids = {name:'john',extra:{city:'London',hobbies:[{id:'football',team:'ABC',},{id:'basketball',team:'DEF',},],},}
const sports = [{name:'volleyball',coach:'tom',},{name:'waterpolo',coach:'jack',},{name:'swimming',coach:'kate',},{name:'football',coach:'sara',},];
const sportsInHobbies = kids.extra.hobbies.map(({ id }) => id);
const result = sports.map((sportObj) => {
const available = sportsInHobbies.includes(sportObj.name);
return available ? {...sportObj, available } : { ...sportObj };
});
console.log(result);
How can I filter an array with a deeply nested array? Given the following 2 arrays, I need the result to be an array with only the rice cakes and the gluten-free-pizza objects:
const foodsILike = ['gluten-free', 'carb-free', 'flavor-free'];
const foodsAvailable = [
{ name: 'pasta', tags: ['delicious', 'has carbs']},
{ name: 'gluten-free-pizza', tags: ['gluten-free']},
{ name: 'pizza', tags: ['delicious', 'best meal of the year']},
{ name: 'rice cakes', tags: ['flavor-free']}
]
I tried the following which just returns everything (the 4 objects):
var result = foodsAvailable.filter(function(food) {
return foodsILike.filter(function(foodILike) {
return foodILike === food;
})
})
result
// Array [ Object, Object, Object, Object]
You could use Array#some and Array#includes for checking if foodsILike contains a tag.
const foodsILike = ['gluten-free', 'carb-free', 'flavor-free'];
const foodsAvailable = [{ name: 'pasta', tags: ['delicious', 'has carbs'] }, { name: 'gluten-free-pizza', tags: ['gluten-free'] }, { name: 'pizza', tags: ['delicious', 'best meal of the year'] }, { name: 'rice cakes', tags: ['flavor-free'] }]
var result = foodsAvailable.filter(function(food) {
return food.tags.some(function(tag) {
return foodsILike.includes(tag);
});
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You are missing a length check on your inner filter and you aren't checking the tags array for the value in that inner filter
const foodsILike = ['gluten-free', 'carb-free', 'flavor-free'];
const foodsAvailable = [
{ name: 'pasta', tags: ['delicious', 'has carbs']},
{ name: 'gluten-free-pizza', tags: ['gluten-free']},
{ name: 'pizza', tags: ['delicious', 'best meal of the year']},
{ name: 'rice cakes', tags: ['flavor-free']}
]
var result = foodsAvailable.filter(function(food) {
return foodsILike.filter(function(foodILike) {
// is value in tags array?
return food.tags.indexOf(foodILike) >-1;
}).length;// length acts as boolean, any length greater than zero is truthy
});
console.log(result)
You can use array.reduce to reduce your array into desired structure, by checking for the condition i.e. foodsILike contains the tag or not.
The benefit of using array.reduce is that you can modify the structure if you want in the reduce loop itself.
const foodsILike = ['gluten-free', 'carb-free', 'flavor-free'];
const foodsAvailable = [
{ name: 'pasta', tags: ['delicious', 'has carbs']},
{ name: 'gluten-free-pizza', tags: ['gluten-free']},
{ name: 'pizza', tags: ['delicious', 'best meal of the year']},
{ name: 'rice cakes', tags: ['flavor-free']}
];
let foods = foodsAvailable.reduce((foods, food) => {
if(foodsILike.some(f => food.tags.indexOf(f) !== -1)) {
foods.push(food);
}
return foods;
},[]);
console.log(foods);
I have an array of objects that I want to filter by comparing a nested property to a search term.
For example:
var array = [
{category: 'Business'
users: [
{name: 'Sally'
tags: [{tag: 'accounting'}, {tag: 'marketing'},...]
},
{name: 'Bob'
tags: [{tag: 'sales'}, {tag: 'accounting'},...]
}...
]
},
{category: 'Heritage'
users: [
{name: 'Linda'
tags: [{tag: 'Italy'}, {tag: 'Macedonia'},...]
},
{name: 'George'
tags: [{tag: 'South Africa'}, {tag: 'Chile'},...]
},...
]
},...
[
Essentially I want to filter the base array of objects by a search terms that include characters from the tag property string in the nested objects 2 arrays down.
So a search for 'market' would result in
[
{category: 'Business'
users: [
{name: 'Sally'
tags: [{tag: 'accounting'}, {tag: 'marketing'},...]
},
{name: 'Bob'
tags: [{tag: 'sales'}, {tag: 'accounting'},...]
}...
]
}
]
Thank you.
You could use Array#filter with looking into the nested arrays by using Array#some.
If the tag is found in a nested array, then iteration stops and the result is given back to the filter callback.
var array = [{ category: 'Business', users: [{ name: 'Sally', tags: [{ tag: 'accounting' }, { tag: 'marketing' }] }, { name: 'Bob', tags: [{ tag: 'sales' }, { tag: 'accounting' }] }] }, { category: 'Heritage', users: [{ name: 'Linda', tags: [{ tag: 'Italy' }, { tag: 'Macedonia' }] }, { name: 'George', tags: [{ tag: 'South Africa' }, { tag: 'Chile' }] }] }],
tag = 'marketing',
result = array.filter(a => a.users.some(u => u.tags.some(t => t.tag.includes(tag))));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The solution using Array.prototype.some() function:
var arr = [{ category: 'Business', users: [{ name: 'Sally', tags: [{ tag: 'accounting' }, { tag: 'marketing' }] }, { name: 'Bob', tags: [{ tag: 'sales' }, { tag: 'accounting' }] }] }, { category: 'Heritage', users: [{ name: 'Linda', tags: [{ tag: 'Italy' }, { tag: 'Macedonia' }] }, { name: 'George', tags: [{ tag: 'South Africa' }, { tag: 'Chile' }] }] }],
search_key = 'market',
result = [];
arr.forEach(function(o){
if (o.users.some(function(v){
return v.tags.some(function(i){ return i.tag.indexOf(search_key) !== -1; });
})) {
result.push(o);
}
});
console.log(result);
Try this:
function search(term){
return
Array.filter(array,function(item){
return JSON.stringify(obj).indexOf(term)!=-1;
});
}
So :
console.log(search('market'));
I hope to be helpful for you:)
The concatAll and concatMap definitions are taken from http://reactivex.io/learnrx/
Array.prototype.concatAll = function() {
var results = [];
this.forEach(function(subArray) {
results.push.apply(results, subArray);
});
return results;
};
Array.prototype.concatMap = function(projectionFunctionThatReturnsArray) {
return this.
map(function(item) {
return projectionFunctionThatReturnsArray(item);
}).
// apply the concatAll function to flatten the two-dimensional array
concatAll();
};
function filterByTags(keyword) {
return array.filter(function (item) {
var allTags = item.users.concatMap(function (user) {
return user.tags.map(function (tag) {
return tag.tag;
});
});
return allTags.some(function (tag) {
return tag.indexOf(keyword) > -1;
});
});
}
console.log(filterByTags('market'));
Of course you could inline the allTags variable for more conciseness.
The filter applied to the initial array will return all items that have users whose tags contain the keyword supplied. The strategy is to build a flattened version of the users' tags and apply some on that.
You can use array.filter like this:
function getFiltered(val) {
return array.filter(category == val);
}
This function will return a new array instance, only with the category keys you passed as the val params.
Note: I am taking a shortcut-like approach to this, primarily to provide a different perspective to the problem.
Instead of deep-searching the properties and arrays under the main array, you can create a json string of the users property and search within that. So I have created a new property usersString that temporarily stores the JSON string of the value against users property.
item.usersString = JSON.stringify(item.users);
Now, this would not be a perfect implementation, but it would almost always work. Also, if you stored this property within the browser (without storing it back to the DB), and used it to quick-search for every time user searches, I think it would be more performant that deep-searching entire array.
var array = [{
category: 'Business',
users: [{
name: 'Sally',
tags: [{
tag: 'accounting'
}, {
tag: 'marketing'
}]
},
{
name: 'Bob',
tags: [{
tag: 'sales'
}, {
tag: 'accounting'
}]
}
]
},
{
category: 'Heritage',
users: [{
name: 'Linda',
tags: [{
tag: 'Italy'
}, {
tag: 'Macedonia'
}]
},
{
name: 'George',
tags: [{
tag: 'South Africa'
}, {
tag: 'Chile'
}]
}
]
}
];
var key = "market";
// Convert the users property into a string - so that it works as a quick search target.
array.forEach(function(item) {
item.usersString = JSON.stringify(item.users);
});
var filteredItems = array.filter(function(item) {
return item.usersString.toLowerCase().indexOf(key.toLowerCase()) >= 0;
});
// Delete the usersString property - if required.
filteredItems.forEach(function(item) {
item.usersString = undefined;
// Or,
// delete item.usersString;
})
console.log(filteredItems);
I have my original objects as follow. All I need is to just extract few properties from existing one and create new object.
var data = [{
id: 3,
name: Axe,
location: alkt
}, {
id: 5,
name: Roy,
location: grelad
}]
I need my output as,
var data_new = [{
id: 3,
name: Axe
}, {
id: 5,
name: Roy,
}]
How to implement in underscore js or any simple method. Possible its a large JSON object.
If there are just few properties you want to extract then simple Array.prototype.map will works fine:
var data = [{
id: 3,
name: 'Axe',
location: 'alkt'
}, {
id: 5,
name: 'Roy',
location: 'grelad'
}]
var result = data.map(function(obj) {
return {
id: obj.id,
name: obj.name
};
});
alert(JSON.stringify(result, null, 4));
Use pick in undescorejs http://underscorejs.org/#pick
Or omit http://underscorejs.org/#omit
_.pick({name: 'moe', age: 50, userid: 'moe1'}, 'name', 'age');
=> {name: 'moe', age: 50}
_.pick({name: 'moe', age: 50, userid: 'moe1'}, function(value, key, object) {
return _.isNumber(value);
});
=> {age: 50}
It you want remove each item's location
var data_new = _.map(data, function(item) {
return _.omit(item, 'location');
});
If all you want is remove properties from objects in an array, you could just delete them while iterating with forEach:
var data_new = data;
data_new.forEach(function(obj){ delete obj.location; /* or any other */ });
$scope.data_new = [];
for(i in $scope.data){
$scope.data_new.push(
{ id: $scope.data[i].id, name: $scope.data[i].name }
)
}