var has = {
name: 'dog',
surname: 'cat',
skills : {
football: true,
basketball: true,
volleyball: true
}
}
var pas = {
name: 'dolphin',
surname: 'cat',
skills : {
football: false,
basketball: false,
volleyball: true
}
}
function compare(Obj1, Obj2) {
var values1 = Object.values(Obj1);
var values2 = Object.values(Obj2);
var equivalent = [];
for (var i = 0; i < values1.length; i++) {
for (var j = 0; j < values2.length; j++) {
if (i === j ) {
equivalent.push(values1[i]);
}
}
}
console.log(equivalent, values1, values2);
}
compare(has, pas);
I am trying to compare 2 objects by their values using nested for loops. It might not be the best method to use for such a simple comparison but as a new starter, I really would like to know why it's not working properly.
There are some mistakes in your code. First of all, you are comparing i with j, when you actually want to compare some values.
I'd solve the problem by taking the keys of the object and iterating among them. If both the objects have those keys AND the values are the same, I'll add them to the equivalent array.
var has = {
name: 'dog',
surname: 'cat',
skills: {
football: true,
}
}
var pas = {
name: 'dolphin',
surname: 'cat',
skills: {
football: true,
}
}
function compare(Obj1, Obj2) {
var values1 = Object.values(Obj1);
var values2 = Object.values(Obj2);
var equivalent = [];
var keys = Object.keys(Obj1);
keys.forEach(k => {
if (Obj1.hasOwnProperty(k) && Obj2.hasOwnProperty(k)) {
if (Obj1[k] === Obj2[k]) {
equivalent.push(Obj1[k]);
}
}
});
console.log(equivalent);
}
compare(has, pas);
However, this won't work for nested objects, as in your case. Why? Because even if the nested objects (skills in your case) have the same values, they are different objects, different references, so the comparison will fail. See how objects comparison in js works here or here.
In this case, a general recursive solution may work like the one below:
var has = {
name: 'dog',
surname: 'cat',
skills: {
football: true,
basketball: true,
volleyball: true
}
}
var pas = {
name: 'dolphin',
surname: 'cat',
skills: {
football: true,
basketball: false,
volleyball: false
}
}
function compare(Obj1, Obj2) {
var values1 = Object.values(Obj1);
var values2 = Object.values(Obj2);
var equivalent = [];
var keys = Object.keys(Obj1);
keys.forEach(k => {
if (Obj1.hasOwnProperty(k) && Obj2.hasOwnProperty(k)) {
if (typeof Obj1[k] === 'object') {
let recursiveAnswer = compare(Obj1[k], Obj2[k]);
equivalent.push(...recursiveAnswer);
} else if (Obj1[k] === Obj2[k]) {
equivalent.push(Obj1[k]);
}
}
});
return equivalent;
}
let equiv = compare(has, pas);
console.log(equiv);
Here, if I encounter a value that is also an object, I call the function again. The partial answer will be pushed once the recursive call is done. You'll see that the final answer will also contain the true value here, value that is from the football field, since they are equal (in my example). Having true/false values in the array among the cat string may be confusing, so you can actually add the key name instead of the value for the bool fields.
You could modify the if and the push like this:
if (typeof Obj1[k] === 'boolean') {
equivalent.push(`(${k}, ${Obj1[k]})`);
} else {
equivalent.push(Obj1[k]);
}
This will produce the following output:
[
"cat",
"(football, true)"
]
Let me know if you have any questions.
Cheers!
Related
My object:
"hockey": {
stats: {
skaters: {
regular: [
{name: "stat1", key: "statkey1"}
{name: "stat2", key: "statkey2"}
{name: "stat3", key: "statkey3"}
]
},
goalies: {
regular: [
{name: "stat1", key: "statkey4"}
{name: "stat2", key: "statkey5"}
{name: "stat3", key: "statkey6"}
]
}
}
}
My code:
var stats = [];
var key = "";
for (position in sport.stats) {
for (stat_group in position) {
for (stat in stat_group) {
key = stat.key;
stats[key] = true;
}
}
}
I'm trying to use the above code to grab the property key from each object located within sport.stats.position.stat_group. Each sport has a different number of positions and stat groups, hence the triple for loop. I'm not getting any console errors it just isn't grabbing the key at all and the iterator variables aren't evaluating to objects but integers.
Here's what I want the resulting stats object to be:
{
"statkey1": true,
"statkey2": true,
"statkey3": true,
...
}
Hope you guys can help! Thanks!
The JS for...in loop iterates through the keys, not values. If you want to iterate an object fully, you can do so like this:
for (key in sports.stats) {
var position = sports.stats[key];
for (group_key in position) {
var stat_group = position[group_key];
for (stat_key in stat_group) {
stat_group[stat_key] = true;
}
}
}
For...in in javascript gives you the key of the object, not the value.
According to your logic, this is what you meant to do:
var stats = {};
var key = "";
for (position in sport.stats) {
for (stat_group in sport.stats[position]) {
for (stat in sport.stats[position][stat_group]) {
key = sport.stats[position][stat_group][stat].key;
stats[key] = true;
}
}
}
Essentially, I want to implement the following:
var categories = [];
var products = // some array of product objects
products.map(function(value) {
if(categories.indexOf(value.Category === -1)) categories.push(value.Category);
});
As result, categories array contains unique list of product categories.
I feel that there should be a better way to do it, but nothing comes to mind.
If there isn't then probably there is no point to use map() in the first place. I could do as simple as
var categories = [];
var products = // some array of product objects
for (var i = 0; i < products.length; i++) {
if(categories.indexOf(products[i].Category === -1)) categories.push(products[i].Category);
}
UPDATE for those who insist it's a duplicate of "how to make an array unique" question. I saw that post, and for my situation I don't think it applies. I don't have an array of values that I need to make unique. I have an array of objects and I need to build an array of unique values. The difference might be subtle - but to get to the use case of that topic I would build a non-unique array and then make it unique. Seems even worse than my original solution
you can use reduce instead of map
var products = [{Category:'vegetable', price: 1}, {Category:'fruits', price: 2}];
var categories = products.reduce(function(sum, product) {
if(sum.indexOf(product.Category) === -1){
sum.push(product.Category);
}
return sum;
}, []);
Update: A solution with Array.prototype.reduce()
var products = [{ Name: 'milk', price: 2.50, Category: 'groceries' }, { Name: 'shirt', price: 10, Category: 'clothing' }, { Name: 'apples', price: 5, Category: 'groceries' }],
categories = products.reduce(function (r, a) {
if (!~r.indexOf(a.Category)) {
r.push(a.Category);
}
return r;
}, []);
document.write('<pre>' + JSON.stringify(categories, 0, 4) + '</pre>');
map all the values of the object categories out first, then use filter to dispose of the duplicates.
var products = [
{ category: 'A' },
{ category: 'B' },
{ category: 'A' },
{ category: 'D' }
];
var categories = products.map(function (e) {
return e.category;
}).filter(function (e, i, a) {
return a.indexOf(e) === i;
}); // [ "A", "B", "D" ]
DEMO
Follow the Below SO Answer:
How to get distinct values from an array of objects in JavaScript?
var flags = [], output = [], l = array.length, i;
for( i=0; i<l; i++) {
if( flags[array[i].age]) continue;
flags[array[i].age] = true;
output.push(array[i].age);
}
Suppose I have an array of objects with some sort of groupable key:
var people = [
{ 'name':'Alice', 'gender': 'female' },
{ 'name':'Bob', 'gender': 'male' },
{ 'name':'Jeremy', 'gender': 'male' },
{ 'name':'Jess', 'gender': 'female' },
{ 'name':'Seymour', 'gender': 'female' },
];
Is there a (native) function/method that can be applied to the array to 'unzip' the array into two arrays, like so:
boysAndGirls = people.[**something**]('gender');
That could result in:
{
'male': [ ... ],
'female': [ ... ]
}
or even:
[
[ {Bob, ...}, {Jeremy, ...}, {Seymour, ...} ], // 'males' array
[ {Alice, ...}, {Jess, ...} ] // 'female' array
]
I could write this algorithm myself, but I really just want to know if there is a native array method -- or one that might exist in another language that could be polyfilled in?
const groupByAge = users.reduce((p,c) =>{
const genKey = Math.floor(c.age/10);
const key = `${genKey}0- ${genKey}9`;
if(!p[key]){
p[key] =[];
}
p[key].push(c);
return p;
}, {})
console.log(groupByAge);
There is no such method in JavaScript. Ruby has it in Enumerator#group_by:
people.group_by { |person| person['gender'] }
and it is easy enough to write in JavaScript as well. In fact, some libraries have it already, e.g. Lodash.
_.groupBy(people, function(person) {
return person['gender'];
});
If you write it yourself, you can customise it a bit:
function groupByProp(array, prop) {
var result = {};
array.forEach(function(item) {
var val = item[prop];
if (!result[val]) result[val] = [item];
else result[val].push(item);
});
return result;
}
groupByProp(people, 'gender');
There is not a native Javascript function for this but you can use the following code:
var originalArray = [1,2,3,4,5,6,7,8,9];
var splitArray = function (arr, size) {
var arr2 = arr.slice(0),
arrays = [];
while (arr2.length > 0) {
arrays.push(arr2.splice(0, size));
}
return arrays;
}
splitArrays = splitArray(originalArray, 2);
console.log(splitArrays);
The nearest thing I can think of off the top of my head for a native solution is to use reduce. It's not as simple as what you are looking for but it works:
var boysAndGirls = people.reduce(function(obj, item) {
obj[item.gender].push(item.name);
return obj;
}, {male: [], female: []});
In my application i want to splice objects from an array upon matching, I am using lodash function for splicing like as shown below, unfortunately the json is not splicing correctly,
Working Demo
Can anyone give me some suggestion for this issue
var arr = [{
name: 'Jack',
id: 125
}, {
name: 'Jack',
id: 125
}];
var result = _.without(arr, _.findWhere(arr, {name: 'Jack'}));
console.log(JSON.stringify(result));
Expected result
[]
Actual Result
[{"name":"Jack","id":125}]
Update 1
Even using normal JavaScript way also giving the same output
for(var i = 0; i < arr.length; i++) {
var obj = arr[i];
if(obj.name === 'Jack') {
arr.splice(i, 1);
}
}
#1
var arr = [{
name: 'Jack',
id: 125
}, {
name: 'Jack',
id: 125
}];
var result = _.rest(arr, function (el) {
return el.name === 'Jack';
});
console.log(JSON.stringify(result)); // "[]"
#2
var arr = [{
name: 'Jack',
id: 125
}, {
name: 'Jack',
id: 125
}, {
name: 'foo',
id: 124
}];
var result = _.rest(arr, function (e) {
return e.name === 'Jack';
});
console.log(JSON.stringify(result)); // "[{\"name\":\"foo\",\"id\":124}]"
// 3 also you can use _.filter if you do not want slice of array...
var result = _.filter(arr, function (e) {
return e.name !== 'Jack';
});
console.log(JSON.stringify(result)); // "[{\"name\":\"foo\",\"id\":124}]"
_.findWhere returns only the first matching element. So that you can use _.difference and _.filter or _.rest to do the task
_.difference(arr, _.filter(arr,function(d){ return d.name = 'Jack' }));
You can implement the same using pure javascript using the code below.
for(var i = 0; i < arr.length; i++) {
var obj = arr[i];
if(obj.name === 'Jack') {
arr.splice(i, 1);
i--; // Splicing of elements will cause shifting of indices in the array
}
}
Have data that has this kind of structure:
$input = [ { animal: 'cat', name: 'Rocky', value: 1 },
{ animal: 'cat', name: 'Spot', value: 2 },
{ animal: 'dog', name: 'Spot', value: 3 } ];
Need fastest possible method for converting to this format:
$output = { animal: [ 'cat', 'dog' ],
name: [ 'Rocky', 'Spot' ],
value: [ 1, 2, 3 ] };
The output should have keys equal to each of the keys in each object from the input. And the output values should be arrays with the sorted unique values. I found a few ways to do it using nested loops, but slower than I would like. With 30,000 elements to the input array with 8 keys for each of the objects, the best I have been able to do is 300ms in Chrome. Would like to get down to 100ms. Is there any faster method using a map or reduce?
Yet another way for modern browsers:
$input.reduce(function(acc, obj) {
Object.keys(obj).forEach(function(k) {
acc[k] = (acc[k] || []).concat(obj[k])
})
return acc
},{})
Here's one way.
$input = [ { animal: 'cat', name: 'Rocky', value: 1 },
{ animal: 'cat', name: 'Spot', value: 2 },
{ animal: 'dog', name: 'Spot', value: 3 } ];
$output = {animal:{},name:{},value:{}};
$input.forEach(function(v,i) {
$output.animal[v.animal] = 1;
$output.name[v.name] = 1;
$output.value[v.value] = 1;
});
$output.animal = Object.keys($output.animal);
$output.name = Object.keys($output.name);
$output.value = Object.keys($output.value);
It prevents having to test each Array every time. You can performance compare to see if it helps.
live example: http://jsfiddle.net/TJVtj/1/
If you don't want to hardcode the keys, you can make the solution generic.
var keys = Object.keys($input[0]),
$output = {};
keys.forEach(function(v) {
$output[v] = {};
});
$input.forEach(function(v) {
keys.forEach(function(vv) {
$output[vv][v[vv]] = 1;
});
});
keys.forEach(function(v) {
$output[v] = Object.keys($output[v]);
});
live example: http://jsfiddle.net/TJVtj/2/
Warning. All the values will be strings since they're fetched as object keys.
function inArray(needle, haystack) {
var length = haystack.length;
for(var i = 0; i < length; i++) {
if(haystack[i] == needle) return true;
}
return false;
}
Above function is used to check duplicates
$output={};
for(i=0; i< $input.length; i++)
{
if(!$output.animal) $output.animal=[];
if(!$output.name) $output.name=[];
if(!$output.value) $output.value=[];
var ani=$input[i];
if(ani.animal && !inArray(ani.animal, $output.animal)) $output.animal.push(ani.animal);
if(ani.name && !inArray(ani.name, $output.name)) $output.name.push(ani.name);
if(ani.value) $output.value.push(ani.value);
}
DEMO.
// If you don't know the objects all have the same keys you need to look at each one-
var output= {},
input= [{
animal:'cat', name:'Rocky', value:1
},{
animal:'cat', name:'Spot', value:2
},{
animal:'dog', name:'Spot', value:3
}];
input.forEach(function(itm){
for(var p in itm){
if(itm.hasOwnProperty(p)){
if(!output[p]) output[p]= [];
if(output[p].indexOf(itm[p])== -1) output[p].push(itm[p]);
}
}
});
Run.expose(output)// nonstandard object to string method
// returned value: (String)
{
animal:[
'cat',
'dog'
],
name:[
'Rocky',
'Spot'
],
value:[
1,
2,
3
]
}
Try Underscore, it's magnificent with this kind of tasks)