Related
thanks for taking a look at this. Sorry for length, trying to be clear!
WHAT I'M TRYING TO DO:
I have an array of users (each user an object) and am trying to filter the users on multiple criteria ("males from France" OR "females from Spain and United States with Engineering skills" etc) but it's proven beyond my skills so far.
The hard part has been that the users are objects within a User array, but within each user object, some values are additional objects or arrays. Here's what the user data array looks like (abbreviated):
let users = [
{
gender: 'male',
location: {street: 'Clement Street', country: 'United States'},
skills: ['engineering', 'underwater'],
}, ...
Notice gender is just a normal property/value but country is within a location object and skills are within an array.
I already have a search button interface that creates toggle buttons to search on each criteria available, and every time you click a button, I add or remove that criteria in a filter object. The filter object looks like this, and uses arrays inside it so that I can define multiple criteria at once, like multiple countries to search, multiple skills, etc.:
filter: {
gender: ['female'],
location: {
country: ['Spain'],},
skills: ['optics', ]
},
WHERE I REALLY GET STUCK
I've created a filterData method that can successfully filter based on Gender (male or female) but can't get it to ALSO filter on country (within the location object) or skills (within the skills array). My current filterData method only goes through one iteration per user, but I've tried For loops and forEach to try to go through each of the filter's criteria ('Spain', 'Optics'), but it just doesn't work. I only get gender.
I think I have two problems: 1) somehow conveying in the code that the item 'key' in some cases will not be a value, but an object or array that must also be searched within, and 2) creating some kind of looping behavior that will go through each of the filter criteria, instead of stopping after the first one (gender).
That's apparently over my head right now, so any guidance or suggestions would be appreciated, thanks very much! And here's all the code I've been working with, including my filterData method.
var filtering = {
filter: {
gender: ["female"],
location: {
country: ["Spain"],
},
skills: ["optics"],
},
users: [
{
gender: "male",
name: "John",
location: { street: "Clement Street", country: "United States" },
skills: ["engineering", "underwater"],
},
{
gender: "female",
name: "Mary",
location: { street: "5th Avenue", country: "Spain" },
skills: ["confidence", "optics"],
},
{
gender: "male",
name: "David",
location: { street: "Vermont Ave", country: "France" },
skills: ["cards", "metalurgy", "confidence"],
},
{
gender: "female",
name: "Rachel",
location: { street: "Vermont Ave", country: "France" },
skills: ["disguise", "electrical"],
},
{
gender: "female",
name: "Muriel",
location: { street: "Vermont Ave", country: "Germany" },
skills: ["flight", "surveillance"],
},
],
filterData: (filter) => {
const filteredData = filtering.users.filter((item) => {
for (let key in filter) {
if (!filter[key].includes(item[key])) {
return false;
}
return true;
}
});
console.log(filteredData);
},
};
filtering.filterData(filtering.filter);
There's a nifty trick called recursion, which is a function calling itself.
The updated code are: checkUserand
filterData
var filtering = {
filter: {
gender: ["female"],
location: {
country: ["Spain"],
},
skills: ["optics"],
},
users: [
{
gender: "male",
name: "John",
location: { street: "Clement Street", country: "United States" },
skills: ["engineering", "underwater"],
},
{
gender: "female",
name: "Mary",
location: { street: "5th Avenue", country: "Spain" },
skills: ["confidence", "optics"],
},
{
gender: "male",
name: "David",
location: { street: "Vermont Ave", country: "France" },
skills: ["cards", "metalurgy", "confidence"],
},
{
gender: "female",
name: "Rachel",
location: { street: "Vermont Ave", country: "France" },
skills: ["disguise", "electrical"],
},
{
gender: "female",
name: "Muriel",
location: { street: "Vermont Ave", country: "Germany" },
skills: ["flight", "surveillance"],
},
],
checkUser (filter, to_check) {
if (Array.isArray(filter))
{
return Array.isArray(to_check)
? filter.some(val => to_check.includes(val)) // if what we're checking is an array
: filter.includes(to_check); // otherwise it's a singular value
}
else
{
const all_checks = []; // this is to save every return value from the recursive function
for (let key in filter) // going through each key in the filter
{
const checked = this.checkUser(filter[key], to_check[key]) // passing two values, which will be compared with each other
all_checks.push(checked) // pushing the checked result
}
return all_checks.every(val => val) // checking that it passes the filter by ensuring every value is true
}
},
filterData () {
let filter = this.filter
return this.users.filter(user => this.checkUser(filter, user))
},
};
// filtering.filterData(filtering.filter);
// filtering.checkUser(filtering.filter, filtering.users[0])
const result = filtering.filterData()
console.log(result)
Bit complex data structure, you should clean. However, solved what expected.
const mergeFilter = (item, [key, value]) => {
let val = Array.isArray(item[key]) ? item[key] : [item[key]];
let m = value[0];
if (typeof value === "object" && !Array.isArray(value)) {
const k2 = Object.keys(value);
val = item[key][k2];
m = value[k2][0];
}
return val.includes(m);
};
const filterData = (users, filter) => {
const filters = Object.entries(filter);
const result = users.reduce((arr, item) => {
let found = filters.every(mergeFilter.bind(null, item));
if (found) arr.push(item);
return arr;
}, []);
return result;
};
var filtering = {"filter":{"gender":["female"],"location":{"country":["Spain"]},"skills":["optics"]},"users":[{"gender":"male","name":"John","location":{"street":"Clement Street","country":"United States"},"skills":["engineering","underwater"]},{"gender":"female","name":"Mary","location":{"street":"5th Avenue","country":"Spain"},"skills":["confidence","optics"]},{"gender":"male","name":"David","location":{"street":"Vermont Ave","country":"France"},"skills":["cards","metalurgy","confidence"]},{"gender":"female","name":"Rachel","location":{"street":"Vermont Ave","country":"France"},"skills":["disguise","electrical"]},{"gender":"female","name":"Muriel","location":{"street":"Vermont Ave","country":"Germany"},"skills":["flight","surveillance"]}]}
const result = filterData(filtering.users, filtering.filter);
console.log(result)
here i state with data
state = {
Response: [
{
"id": "15071",
"name": "John",
"salary": "53",
"age": "23",
"department": "admin"
},
{
"id": "15072",
"name": "maxr",
"salary": "53",
"age": "23",
"department": "admin"
},
{
"id": "15073",
"name": "Josef",
"salary": "53",
"age": "23",
"department": "admin"
},
{
"id": "15074",
"name": "Ye",
"salary": "53",
"age": "23",
"department": "admin"
}
]
i am displaying these records in the table. In table u will see 10 records and there will be a button on top of table so if append button is pressed then 10 records has to be added on every button press and the data has to be same but it has to be appended using the below logic i am trying to set the state by pushing 10 records and trying to append it for ex if i have 1,2,3,4,5,6,7,8,9,10 if i press append 1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10 has to be apeended
appendEmployees() {
var reLoadCount = 1;
for (let i = 0; i < 10; i++) {
const myObj = {
id: 0,
name: '',
salary: 0,
department: ''
};
myObj.id = +this.setState.employee[i].id + (reLoadCount * 10);
myObj.name = this.setState.employee[i].name;
myObj.salary = this.setState.employee[i].salary;
myObj.department = this.setState.employee[i].department;
this.setState.employee.push(myObj);
}
reLoadCount++;
}
am i doing some thing wrong here
If I get this right you're trying to add 10 duplicates of the objects in the this.state.employee array, the only difference between these new objects and the existing ones is their id.
If that is the case, here is how you can do that:
appendEmployees() {
this.setState(prevState => {
// Get the biggest ID number.
const maxId = Math.max(...prevState.employee.map(e => parseInt(e.id)));
// create 10 new employees copies of the first 10.
const newEmployees = prevState.employee.slice(0, 10).map((e, i) => ({
...e,
id: (maxId + i + 1)
}));
// return/update the state with a new array for "employee" that is a concatenation of the old array and the array of the 10 new ones.
return {
employee: [...prevState.employee, ...newEmployees]
}
});
}
I've added some comments to the example to explain what it does.
The important thing is this.setState which is the function used to update the state, here, I've used it with a function as the first parameter (it works with objects as well), I've did that because it is the preferred way of generating a new state that is derived from the old state.
Here's a list of parents and I want to sort the parents by their 2nd's child's age with ramda:
[
{
name: "Alicia",
age: "43",
children: [{
name: "Billy",
age: "3"
},
{
name: "Mary",
age: "8"
},
]
},
{
name: "Felicia",
age: "60",
children: [{
name: "Adrian",
age: "4"
},
{
name: "Joseph",
age: "5"
},
]
}
]
How do I do on about it? I tried doing something along the lines of
parents.sort(
sortBy("-children.age"))
);
Use R.sortBy and extract the value with a function create with R.pipe. The function gets the children array of the object with R.prop, takes the last child (R.last), gets the age with R.propOr (returns 0 if no children), and converts to a Number. You can use R.negate if you want to reverse the order.
const { sortBy, pipe, prop, last, propOr } = R
const fn = sortBy(pipe(
prop('children'),
last,
propOr(0, 'age'),
Number,
// negate - if you want to reverse the order
))
const parents = [{"name":"Alicia","age":"43","children":[{"name":"Billy","age":"3"},{"name":"Mary","age":"8"}]},{"name":"Felicia","age":"60","children":[{"name":"Adrian","age":"4"},{"name":"Joseph","age":"5"}]}]
const result = fn(parents)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
In vanilla JavaScript (making some assumptions about the relatively poorly formatted input) using the Array.prototype.sort method:
let parents = [ .... ]; // What you have above
parents = parents.sort((a, b) => {
return a.children[1].age - b.children[1].age; // Change - to + for ascending / descending
});
Be careful though - what would happen if a parent had fewer than 2 children?
Assuming your JSON above was hand generated, including the syntax errors, then assuming your real data is just fine (an array of parents, with each parent having a children array of objects) then a normal JS sort will work just fine:
const compareC2(parent1, parent2) {
let c1 = parent1.children;
let c2 = parent2.children;
if (!c1 || !c2) {
// what happens if someone has no children?
}
let l1 = c1.length;
let l2 = c2.length;
if (l1 === 0 || l2 === 0) {
// different symptom, but same question as above
}
if (l1 !== l2) {
// what happens when the child counts differ?
}
if (l1 !== 2) {
// what happens when there are fewer, or more than, 2 children?
}
// after a WHOLE LOT of assumptions, sort based on
// the ages of the 2nd child for each parent.
return c1[1].age - c2[1].age;
}
let sorted = parents.sort(compareC2);
I would use sortWith with ascend functions. Using sortWith allows you to define a first sort order function, a second sort order function, etc.
const people = [
{
name: "Alicia",
age: "43",
children: [{
name: "Billy",
age: "3"
},
{
name: "Mary",
age: "8"
},
]
},
{
name: "Felicia",
age: "60",
children: [{
name: "Adrian",
age: "4"
},
{
name: "Joseph",
age: "5"
},
]
}
];
const by2ndChildAge = ascend(pathOr(0, ['children', 1, 'age']));
const by1stChildAge = ascend(pathOr(0, ['children', 0, 'age']));
console.log(sortWith([by2ndChildAge, by1stChildAge], people));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {sortWith, ascend, pathOr} = R;</script>
The simplest solution is, I think, just to combine sortBy with path:
const sortBy2ndChildAge = sortBy(path(['children', 1, 'age']))
const people = [{name: "Alicia", age: "43", children: [{name: "Billy", age: "3"}, {name: "Mary", age: "8"}]}, {name: "Felicia", age: "60", children: [{name: "Adrian", age: "4"}, {name: "Joseph", age: "5"}]}]
console.log(sortBy2ndChildAge(people))
<script src="https://bundle.run/ramda#0.26.1"></script><script>
const {sortBy, path} = ramda </script>
There are several potential flaws with this, that others have noted. Are parents always guaranteed to have at least two children? Do we really want a lexicographic sort -- i.e. '11' < '2' -- or do you want to convert these values to numbers?
It would be easy enough to fix both of these problems: sortBy(compose(Number, pathOr(0, ['children', 1, 'age']))), but that depends upon what you're trying to do. If you're just using this to learn about Ramda, then sortBy and path are both useful functions to know. sortBy is useful when you can convert the items to be sorted to some ordered type -- Strings, numbers, dates, or anything with a numeric valueOf method. You supply that conversion function and a list of values and it will sort by that. path is simply a null-safe read for a list of nested properties in an object.
I want to have a JSON object which contains multiple person objects. Is there a better way to format this? What I did below is have a result key which would be an array consisting of arrays (which have the JSON person object inside of the inner array).
{
result: [
[
{
"name": "Josh",
"age": 15
}
],
[
{
"name": "Joe",
"age": 16
}
]
]
}
Then in the for loop I do something like this:
var nameArray = result[0];
for (var i = 0; i < numberOfObjectsInJSONObject; i++) {
newNameArray.push(nameArray[i].firstPerson);
}
No point in having each person object wrapped inside it's own array. Unless there is some other grouping that needs done all you should need is one array that contains all the person objects
result: [
{
"name": "Josh",
"age": 15
},
{
"name": "Joe",
"age": 16
}
]
This array can be sorted by ages or names, filtered etc
If you want to keep object like that, something like this work. pleases try this one
var pList = {
people: [{
'name': 'Josh',
'age': 15
}, {
'name': 'Joe',
'age': 16
}],
animal: [{
'type': 'Dog',
'legs': 4
}, {
'type': 'Bird',
'legs': 2
}]
};
[].forEach.call(pList.people, function(p) {
// can push its value into new array or else
console.log(`${p.name} -- ${p.age}`);
})
//or
pList.animal.forEach(function(a) {
console.log(`${a.type} -- ${a.legs}`)
})
Here is your list of objects:
var a = [];
a.push({ "name" : "Joe", "age" : 16 });
a.push({ "name" : "John", "age" : 17 });
Here is how you can create JSON:
JSON.stringify(a);
Here is the result:
[{"name":"John","age":17},{"name":"Joe","age":16}]
Here is how you can iterate through your array:
for (var i = 0; i < a.length; i++) { console.log(a[i]) }
I'm trying to accept user input from a form. I can successfully access the input and can print the data from the object to the console, but when I attempt to push this object to the array of Objects I have stored in the controller, it doesn't work.
Here is a small snippet of my code:
$scope.dogs = [
{
name: "Blinky",
age: "2",
owner: "Martha Franklin",
vaccinated: "Y"
},
{
name: "Spot",
age: "4",
owner: "Martha Franklin",
vaccinated: "Y"
}];
$scope.dog = {
name: "",
age: "",
owner: "",
vaccinated: ""
};
$scope.savePet = function(){
//console.log($scope.dog.name); This prints the name in the input HTML form
$scope.dogs.push($scope.dog); //THIS DOESN'T STORE IN THE INFORMATION
};
Like I stated previously, I am attempting to push an object onto the end of an array of objects and it just isn't working. This is all happening within the controller of an AngularJS module.
$scope.dogs = [
{
name: "Blinky",
age: "2",
owner: "Martha Franklin",
vaccinated: "Y"
},
{
name: "Spot",
age: "4",
owner: "Martha Franklin",
vaccinated: "Y"
}];
$scope.dog = {
name: "john",
age: "44",
owner: "rocky",
vaccinated: "n"
};
$scope.savePet = function(){
console.log($scope.dog.name);
$scope.dogs.push($scope.dog); //THIS DOESN'T STORE IN THE INFORMATION
console.log($scope.dogs);
};
$scope.dog value is null so that it is not working...
add value in $scope.dog.
your code is Fine.
The reason is you are trying to add the same object to the array. Array doesn't take the same object. One need to create a new object or replicate (copy) to make it work.
I dont see why this not working for you. I created a JS fiddler to simulate your code and its perfectly working.
http://jsfiddle.net/nirus/vgxg7yzz/2/
Code:
function DogController($scope) {
$scope.dogs = [
{
name: "Blinky",
age: "2",
owner: "Martha Franklin",
vaccinated: "Y"
},
{
name: "Spot",
age: "4",
owner: "Martha Franklin",
vaccinated: "Y"
}];
$scope.dog = {
name: "",
age: "",
owner: "",
vaccinated: ""
};
$scope.savePet = function(){
$scope.dogs.push($scope.dog); //THIS DOESN'T STORE IN THE INFORMATION
console.log($scope.dogs[2]);
};
}
HTML:
<div ng-app ng-controller="DogController">
<div>Hello open your console to see the result</div>
<input type="submit" ng-click="savePet()" value="Click Me"></input>
</div>
Result: See the below.
Suggestion: If its still not working i would suggest you to fork the above code and modify accordingly and post back so that we can have a better look at it.