I have a single array of objects.
The objects are different variations of this:
{
"animals": {
"name": "elephant",
"diet": "herbivore",
"color": "spotted",
"age": "12",
"numbers": ["11", "10", "24", "9"]
}
}
How to iterate through the collection and determine if there are differences between the "numbers" properties or "diet" properties? As in:
In the array, 50 elephant object's "diet" property read "herbivore", but one it reads "omnivore". Break out because it's different and report it.
Or as in:
In the array, 70 elephant object's "numbers" property are the same (even if some are out of order). Report they are the same.
What did I try?:
Stringifying the objects and comparing them, but that won't work because I'm not trying to compare whole objects (just a few individual properties).
I can make this work with a for loop, but it seems stupid overkill and not very efficient for a collection that potentially has hundreds of objects in it.
This is pseudo code, don't freak out:
var isValid = true;
//go through each element in the array
for(let i = 0, i < array.length, i++) {
//check if you have exceeded length of the array
if(i + 1 <= array.length) {
//sort the numbers
var sortedLowerNumbers = sortFunction(array[i].numbers);
var sortedhigherNumbers = sortFunction(array[i+1].numbers);
//compare equality
isValid = json.stringify(sortedLowerNumbers) === json.stringify(sortedhigherNumbers);
if(!isValid) {
break
}
}
}
My research
None of the questions seem applicable. They're seem to be either comparing multiple arrays rather than one or comparing entire JSON objects for equality rather than comparing individual properties.
Is there a smarter way to do this?
Although a for loop is the best and most simple way to do it. if you are looking for brevity you can use .every():
let arr = [{
name: "elephant",
diet: "herbivore",
color: "spotted",
age: "12",
numbers: ["11", "10", "24","9"]
},{ name: "elephant",
diet: "herbivore",
color: "spotted",
age: "12",
numbers: ["11", "10", "24","9"]
},{
name: "elephant",
diet: "herbivore",
color: "spotted",
age: "12",
numbers: ["11", "10", "24","9" , "23"]
}];
let arrSort = arr[0].numbers.sort();
let JSONarrSort = JSON.stringify(arrSort);
console.log(arr.every(x => JSON.stringify(x.numbers.sort()) === JSONarrSort));
console.log(arr.every(x => x.diet === arr[0].diet));
Related
My usage will contain 6 different object types (some which contain double nested arrays), and any possibility of number of entries, on the condition that an given entry is unique.
These objects do not have a consistent unique identifier (a unique identifier is applied in backend on submission).
here is an example of what the array may look like (only 2 object types):
arr = [
{name:"aaa",time:15},
{name:"aaa",time:22},
{timeline: "250", chars[{a},{b},{c}]},
{timeline: "220", chars[{d},{e},{f}]},
]
obj = {name:"aaa",time:22}
My intention is to gain a true or false based on if obj is inside arr
I have tried methods:
I was suggested this method & it errors: #<Object> is not a function
console.log(arr.find(obj))
I also found this suggestion but it will always return false even with the element present
console.log(arr.includes(object))
I tried this method myself, though it will always fail.
console.log(arr.filter((element, index) => element === obj)
With attempt 4, If I was to compare name, this would be insufficient as unique time would be ignored missing valid entries.
If I was to pass every field, this would also not work as each object may or may not have the field and cause error.
Its not really possible to manually pre-filter filter into distinct categories, as every time a new type is added it will need manually adding to the filter.
If there is a library which could do this that you know of, please let me know as that would be perfect. Otherwise any other suggestions (excluding separating arrays) Would be greatly appreciated.
Use arr.some() to check if the required object is present in the array.
To compare the objects, a simpler way is to Stringify both the Objects and compare them.
const arr = [
{name:"aaa",time:15},
{name:"aaa",time:22},
{name: "aaa", chars: ["a", "b", "c"]},
{name: "bbb", chars: ["d", "e", "f"]},
]
const obj1 = {name:"aaa", time: 15}
const obj2 = {name:"aaa",chars: ["a", "b", "c"]}
console.log(arr.some((element) => JSON.stringify(element) === JSON.stringify(obj1))) // true
console.log(arr.some((element) => JSON.stringify(element) === JSON.stringify(obj2))) // true
Didn't give much thought on performance.
I didn't put much thought on performace here but this might help:
function checkObjectInArray(arr, obj) {
const res = arr.some((el) => deepEqual(el, obj));
console.log(res);
}
function deepEqual(obj1, obj2) {
if (Object.keys(obj1).length !== Object.keys(obj2).length) return false;
for (let prop in obj1) {
if (!obj2.hasOwnProperty(prop) || obj2[prop] !== obj1[prop]) {
return false;
}
}
return true;
}
in your case you can use it like:
arr = [
{ name: "aaa", time: 15 },
{ name: "aaa", time: 22 },
{ timeline: "250", data: ["2", "3", "4"] },
{ timeline: "251", data: ["2", "3", "4"] }, // what is chars[{d},{e},{f}] ?!
];
obj = { name: "aaa", time: 22 };
checkObjectInArray(arr, obj);
Observation : arr is not a valid array. Nested chars is not containing a valid value.
Solution : You can simply achieve the requirement by Just converting the JSON object into a JSON string and by comparing.
This solution works fine as you are just trying to find a single object in the passed arr.
Live Demo :
const arr = [
{name:"aaa",time:15},
{name:"aaa",time:22},
{timeline: "250", chars: [{a: 1},{b: 2},{c: 3}]},
{timeline: "220", chars: [{d: 4},{e: 5},{f: 6}]},
];
const obj = {name:"aaa",time:22};
const res = JSON.stringify(arr).indexOf(JSON.stringify(obj)) !== -1 ? true : false;
console.log(res);
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.
So lets say I have some JSON:
{
"users": [{
"name": "bob",
"age": 16,
"likes": ["cats", "kayaking", "knitting"]
}, {
"name": "kyle",
"age": 19,
"likes": ["dogs", "soccer", "baseball"]
}, {
"name": "mike",
"age": 18,
"likes": ["cats", "cars", "kayaking"]
}]
}
and I want to go through that and return all user objects with the likes that include "cats" and "kayaking". I'm using lodash and there doesn't seem to be a lodash method to do that. I only see _.findKey and _.includes. Is there a method or group of methods that I can use that'll do this or am I better off just using vanilla javascript?
You are looking for _.filter():
var output = _.filter(input.users, function(item) {
return _.includes(item.likes, 'cats')
&& _.includes(item.likes, 'kayaking');
});
It filters the array using the filter function you specify. If this filter function returns true for a given item of the array, this item will be included in the resulting array.
If you want to use Vanilla JS:
You can use indexOf along with a comparator array for comparison.
Using this comparator array, you can filter the results to return only the objects that match the criteria.
Additionally use a reduce method to return true or false depending on whether or not one of the strings in the array matches your comparator array.
var comparator = ['dogs', 'baseball'];
var dogsAndBaseball = obj.users.filter(function(obj) {
return obj.reduce(function(acc, cur) {
return acc || comparator.indexOf(cur) > -1;
}, false);
});
This will return
[{
"name": "kyle",
"age": 19,
"likes": ["dogs", "soccer", "baseball"]
}]
I'm still learning javascript patterns, and I'm curious about this one.
If I have an array of objects, should I make the array of objects not have keys (Example A), or use their keys (Example B)?
An array of objects without any key:
var soylent_green_candidates = [{
id: 12341,
name: "Don",
gender: "Male",
weapon_of_choice: "Kangaroo Cannon"
},
{
id: 24325,
name: "Jimmy",
gender: "Male",
weapon_of_choice: "Sushi Blaster"
},
...
]
Or an array of objects using the unique ID's as keys:
var soylent_green_candidates = [
12341: {
name: "Don",
gender: "Male",
weapon_of_choice: "Kangaroo Cannon"
},
24325: {
name: "Jimmy",
gender: "Male",
weapon_of_choice: "Sushi Blaster"
},
...
]
Your second example is invalid, so that should put the question to sleep.
In Javascript you have the option of arrays, which are sorted but essentially "key less". Each array entry can only be accessed by its numeric index, period.
The other option is objects, which have no guaranteed order but are keyed by arbitrary values.
['foo', 'bar', 'baz']
{ foo : 'bar', baz : 42 }
You make the choice based on these two criteria, order and requirement for keys.
(Note: while you can use arbitrary numeric indices for arrays, you can't do so using the literal syntax [..], and it's not usually a good idea. If you need explicit keys, you want objects.)
I'm positive this question must have been covered before, but I can quite find it. So....
I have an object like so
Object
name: Fred
lastname: Jones
city: Los Angeles
I'd like to use Javascript to convert it to a string that looks like this:
//Do want this
[
{"name": "name", "value": "Fred"},
{"name": "lastname", "value": "Jones"},
{"name": "city", "value": "Los Angeles"}
]
All of the examples I've found use JSON.parse() to get a result that looks like this (which I don't want):
//Don't want this
[
{"name": "Fred", "lastname": "Jones", "city": "Los Angeles"}
]
I'm working with another developer who says this is how Jquery parses objects (EDIT- he's using $serializeArray(), so perhaps JQuery has a method to help me with this.
Any ideas would be most welcome.
Thanks!
This conversion calls for iterating through the properties of the source object and accumulating entries in a result array.
function toList( obj ) {
var rv = [], k;
for (k in obj) {
if (obj.hasOwnProperty(k))
rv.push({ name: k, value: obj[k] });
}
return rv;
}
var list = toList( myObject );
var arr = []
for (var key in object_name) {
arr.push({'name': key, 'value': object_name[key]})
}
Loop through the keys and add it to the array.
You can loop through the object properties and create the array
var a = array();
for (p in obj) {
a.push({'name': p, 'value': obj[p]});
}
This should get the structure that you want.
You should be able to do it in a simple for..in loop.
var yourObject = { name: "Fred", lastname: "Jones", city: "Los Angeles" },
parsed = [];
for(var i in yourObject){
parsed.push({ name: i, value: yourObject[i]})
}