Check field in obj - javascript

Good day.
I've got
array a = [
{a:1, value: 2},
{a:2, value: 2},
{a:3, value: 2}
]
How I can check if all objects got same value return true if in one object I got different value return false.

Another option is using new Set() to get all the unique values. If the unique value is only one, it means all values are the same.
let a = [{a:1, value: 2},{a:2, value: 2},{a:3, value: 2}];
let result = [...new Set(a.map(o => o.value))].length === 1;
console.log(result);
To reduce overhead, as per suggested by Nina, you can:
let a = [{a:1, value: 2},{a:2, value: 2},{a:3, value: 2}];
let result = new Set(a.map(o => o.value)).size === 1;
console.log(result);

Just check the first value against all others.
var array = [{ a: 1, value: 2 }, { a: 2, value: 2 }, { a: 3, value: 2 }],
result = array.every(({ value }, _, a) => a[0].value === value);
console.log(result);

There is build int function every. You pass a predicate into it and if every item satisfied predicate - returns true, else - false.
const arr = [{a:1, value: 2},{a:2, value: 2},{a:3, value: 2}];
const result = arr.every(item => item.value === 2);
console.log(result);

You can also use simple logic using for to know if the value matches or not. Also, create a reusable function isMatch(arr) so that it can be reused for multiple array having same structure.
function isMatch(arr){
var val = arr[0].value;
var match = true;
for(var i=1; i<arr.length; i++){
if(val !== arr[i].value){
match = false;
break;
}
}
return match;
}
var a = [{a:1, value: 2},{a:2, value: 2},{a:3, value: 2}];
console.log(isMatch(a));
a = [{a:1, value: 2},{a:2, value: 3},{a:3, value: 2}];
console.log(isMatch(a));

People recommend using every, but I would recommend using some : every iterates over all items of the array, while some stops at the item when the predicate is false. In your example, this doesn't change much, but on large sets, it can improve performances.
try something like this :
const arr = [{a:1, value: 2}, {a:2, value: 2}, {a:3, value: 2}];
const value = arr[0].value;
const areEqual = arr.some(arrItem => arrItem.value === value);
console.log('are all equal : ', areEqual);

For my question help this answer
var answer = new Set(a.map(({value}) => value)).size === 1;

Related

Unable to add key value pairs in Javascript dictionary

I have some REACT code that generates a dictionary for me
My sample code is
var dict = []; // create an empty array
dict.push({
key: "keyName",
value: "the value"
});
and my dictionary looks like this
{key: "2021-03-31", value: 1}
{key: "2021-03-26", value: 1}
{key: "2021-03-27", value: 1}
{key: "2021-03-26", value: 1}
{key: "2021-03-27", value: 1}
Now I am trying to implement a logic like this.
If a key exists in the dictionary then I want to increase the number in the value by 1, or else insert a new key-value pair in the dictionary
var xkey=<<some value>>
if (xkey in dict) {
console.log("key found");
dt[xkey] +=1
}
else
{
dict.push(
{key: xkey
,value: 1 })
}
My If the condition is always failing and the code gets into the else block every time, hence the values for all keys are set to 1 whereas, in my example here, two of the keys should have had the value equal to 2.
My dictionary should have become
{key: "2021-03-31", value: 1}
{key: "2021-03-26", value: 2}
{key: "2021-03-27", value: 2}
Can someone please help me understand what I am doing wrong here?
You used Arrays and Objects in a mixed way. You can not acces an array elements by a key like you did. In other words you declared an Array not a Dictionory (called an Object in Javascript). The in operator only works aa s real members of the object dict.
Try this example:
var dict = {}; // create a map / object
...
var xkey=<<some key>>;
if (xkey in dict)
{
console.log("key found");
dict[xkey] +=1
}
else
{
dict[xkey] = 1;
}
There is also a shorter way.
var dict = {}; // create a map / object
...
var xkey=<<some key>>;
dict[xkey] = (xkey in dict) ? dict[xkey] + 1 : 1;
BTW your code is not compilable.
dict is an array and you probably want to find a specific date by key of a specific object in the dict array for modifying it's value . For finding your specific object in the array you can use simple find() method
let array = [
{ key: "2021-03-31", value: 1 },
{ key: "2021-03-26", value: 1 },
{ key: "2021-03-27", value: 1 },
]
const myKey = '2021-03-26'
let goalObject = array.find(object => object.key === myKey)
goalObject ? goalObject.value++ : array.push({ key: myKey, value: 1 })
You are trying to traverse to all records or else you can find an index and increase the value if the key is present. I have one solution maybe this will helps you to get your answer.
var dict = [];
function createDict(keyval) {
if (dict.length === 0) {
dict.push({
key: keyval,
value: 1
})
} else {
const index = dict.findIndex(x => x.key === keyval);
if (index > -1) {
dict[index].value += 1;
} else {
dict.push({
key: keyval,
value: 1
})
}
}
}
createDict("2021-03-31");
createDict("2021-03-26");
createDict("2021-03-27");
createDict("2021-03-26");
createDict("2021-03-27");
console.log(dict);
Final Results get like you want
[
{ key: '2021-03-31', value: 1 },
{ key: '2021-03-26', value: 2 },
{ key: '2021-03-27', value: 2 }
]
if (xkey in dict)
This is the problem. here dict is an array. So if you will check that x in dict it will look at the keys of an array.
In JavaScript arrays are just type of objects with keys that goes from 0 to length of the array and one extra key is length.
Let's see in your example:
your dict is like this:
dict = [
{key: "2021-03-31", value: 1},
{key: "2021-03-26", value: 1},
{key: "2021-03-27", value: 1},
{key: "2021-03-26", value: 1},
]
So if you run
for(xkey in dict){
console.log(xkey);
}
you will get output like this.
0
1
2
3
length
So if you check that "1" in dict then it will return true.
Solution to your problem might be look like this.
func addValue(newValue){ // newValue is value to add if does not exist and increment if exists
var isExist = false;
for(var i = 0; i < dict.length; i++){
if(dict[i].key === newValue){
dict[i].value++;
isExist = true;
break;
}
}
if(!isExist){
dict.push({key: newValue, value: 1})
}
}
More optimized solution is posted already by #Bhushi-da. This is for better understanding.

Trouble creating array of objects

I have an array and I want to pick 3 of its elements and put store it in another array as a single object and again repeatedly follow the same process on every 3 elements of the array. This is my code below:
let breakpoints = [
{name: "from-amonut", value: "100"},
{name: "to-amonut", value: "200"},
{name: "gst", value: "10"},
{name: "from-amonut", value: "200"},
{name: "to-amonut", value: "300"},
{name: "gst", value: "20"},
{name: "from-amonut", value: "300"},
{name: "to-amonut", value: "400"},
{name: "gst", value: "30"}
];
let temp = {
"from_amount": 0,
"to_amount": 0,
"gst": 0
};
let formattedBreakpoints = [];
breakpoints.map((v, k) => {
(v.name == "from-amonut") ? temp.from_amount = v.value: "";
(v.name == "to-amonut") ? temp.to_amount = v.value: "";
(v.name == "gst") ? temp.gst = v.value: "";
((k + 1) % 3 === 0) ? (formattedBreakpoints.push(temp), console.log(temp)) : "";
});
console.log(formattedBreakpoints);
I'm expecting formattedBreakpoints to be [{"from_amount":100, "to_amount":200, "gst":10}, {"from_amount":200, "to_amount":300, "gst":20}.....] but only returns all the objects with the value of last array elements.
You can use Array.reduce() to add all properties to the last object. Whenever the index (i) remainder from 3 is 0, you add another object to the accumulator.
const breakpoints = [{"name":"from-amonut","value":"100"},{"name":"to-amonut","value":"200"},{"name":"gst","value":"10"},{"name":"from-amonut","value":"200"},{"name":"to-amonut","value":"300"},{"name":"gst","value":"20"},{"name":"from-amonut","value":"300"},{"name":"to-amonut","value":"400"},{"name":"gst","value":"30"}];
const result = breakpoints.reduce((r, { name, value }, i) => {
if(i % 3 === 0) r.push({});
const key = name.replace(/-/g, '_');
r[r.length - 1][key] = value;
return r;
}, []);
console.log(result);
What's the problem in your code is the reuse of temp. Since temp is an object, pushing it to the array, adds the reference to the array, and doesn't create a new object. To prevent that, you can init temp whenever you get to the last property. You should also use Array.forEach() (do something with value) instead of Array.map() (create new array with items based on original values).
const breakpoints = [{"name":"from-amonut","value":"100"},{"name":"to-amonut","value":"200"},{"name":"gst","value":"10"},{"name":"from-amonut","value":"200"},{"name":"to-amonut","value":"300"},{"name":"gst","value":"20"},{"name":"from-amonut","value":"300"},{"name":"to-amonut","value":"400"},{"name":"gst","value":"30"}];
let temp = {};
const formattedBreakpoints = [];
breakpoints.forEach((v, k) => {
if(v.name == "from-amonut") temp.from_amount = v.value;
if(v.name == "to-amonut") temp.to_amount = v.value;
if(v.name == "gst") { // whenever we get to gst, we can push to array, and reset temp
temp.gst = v.value;
formattedBreakpoints.push(temp);
temp = {};
};
});
console.log(formattedBreakpoints);
The problem with your code that you are mutating a single object temp and pushing it 3 times to the result array. So what's going on:
You are creating an object
In a map method you are mutating it repeatedly and after every 3 iterations you are setting reference to this object to the result array.
Key word here is reference. That means that this is the same object. For example:
const result = [];
const obj = {};
obj.a = 'a';
result.push(obj); // => result = [{ a: 'a' }]
obj.b = 'b';
result.push(obj); // => result = [{ a: 'a', b: 'b' }, { a: 'a', b: 'b' }]
Try this way:
let formattedBreakpoints = [];
for (let i = 0; i < breakpoints.length; i += 3) {
const breakPointOptions = breakpoints.slice(i, i + 3);
formattedBreakpoints.push(breakPointOptions.reduce((result, { name, value }) => {
result[name] = value;
return result;
}, {}));
}
let breakpoints = [
{name: "from-amonut", value: "100"},
{name: "to-amonut", value: "200"},
{name: "gst", value: "10"},
{name: "from-amonut", value: "200"},
{name: "to-amonut", value: "300"},
{name: "gst", value: "20"},
{name: "from-amonut", value: "300"},
{name: "to-amonut", value: "400"},
{name: "gst", value: "30"}
]
let temp = {
"from_amount" : 0,
"to_amount" : 0,
"gst" : 0
};
let formattedBreakpoints = [];
breakpoints.map((v, k)=>{
(v.name == "from-amonut") ? temp.from_amount = v.value : "";
(v.name == "to-amonut") ? temp.to_amount = v.value : "";
(v.name == "gst") ? temp.gst = v.value : "";
((k + 1) % 3 === 0) ? (formattedBreakpoints.push(temp), temp = {... temp}) : "" ;
});
console.log(formattedBreakpoints);
As temp is an object you are using it by reference. So you need to create a new object each time you push or else the same object will be referenced. Hence I'm using temp itself to create this new object.

retriving values from javascript object and then convert it to one object

I have a problem! I am creating an rating app, and I have come across a problem that I don't know how to solve. The app is react native based so I am using JavaScript.
The problem is that I have multiple objects that are almost the same, I want to take out the average value from the values of the "same" objects and create a new one with the average value as the new value of the newly created object
This array in my code comes as a parameter to a function
var arr = [
{"name":"foo","value":2},
{"name":"foo","value":5},
{"name":"foo","value":2},
{"name":"bar","value":2},
{"name":"bar","value":1}
]
and the result I want is
var newArr = [
{"name":"foo","value":3},
{"name":"bar","value":1.5},
]
If anyone can help me I would appreciate that so much!
this is not my exact code of course so that others can take help from this as well, if you want my code to help me I can send it if that's needed
If you have any questions I'm more than happy to answer those
Iterate the array with Array.reduce(), and collect to object using the name values as the key. Sum the Value attribute of each name to total, and increment count.
Convert the object back to array using Object.values(). Iterate the new array with Array.map(), and get the average value by dividing the total by count:
const arr = [{"name":"foo","Value":2},{"name":"foo","Value":5},{"name":"foo","Value":2},{"name":"bar","Value":2},{"name":"bar","Value":1}];
const result = Object.values(arr.reduce((r, { name, Value }) => {
if(!r[name]) r[name] = { name, total: 0, count: 0 };
r[name].total += Value;
r[name].count += 1;
return r;
}, Object.create(null)))
.map(({ name, total, count }) => ({
name,
value: total / count
}));
console.log(result);
I guess you need something like this :
let arr = [
{name: "foo", Value: 2},
{name: "foo", Value: 5},
{name: "foo", Value: 2},
{name: "bar", Value: 2},
{name: "bar", Value: 1}
];
let tempArr = [];
arr.map((e, i) => {
tempArr[e.name] = tempArr[e.name] || [];
tempArr[e.name].push(e.Value);
});
var newArr = [];
$.each(Object.keys(tempArr), (i, e) => {
let sum = tempArr[e].reduce((pv, cv) => pv+cv, 0);
newArr.push({name: e, value: sum/tempArr[e].length});
});
console.log(newArr);
Good luck !
If you have the option of using underscore.js, the problem becomes simple:
group the objects in arr by name
for each group calculate the average of items by reducing to the sum of their values and dividing by group length
map each group to a single object containing the name and the average
var arr = [
obj = {
name: "foo",
Value: 2
},
obj = {
name: "foo",
Value: 5
},
obj = {
name: "foo",
Value: 2
},
obj = {
name: "bar",
Value: 2
},
obj = {
name: "bar",
Value: 1
}
]
// chain the sequence of operations
var result = _.chain(arr)
// group the array by name
.groupBy('name')
// process each group
.map(function(group, name) {
// calculate the average of items in the group
var avg = (group.length > 0) ? _.reduce(group, function(sum, item) { return sum + item.Value }, 0) / group.length : 0;
return {
name: name,
value: avg
}
})
.value();
console.log(result);
<script src="http://underscorejs.org/underscore-min.js"></script>
In arr you have the property Value and in newArr you have the property value, so I‘ll assume it to be value both. Please change if wished otherwise.
var map = {};
for(i = 0; i < arr.length; i++)
{
if(typeof map[arr[i].name] == ‘undefined‘)
{
map[arr[i].name] = {
name: arr[i].name,
value: arr[i].value,
count: 1,
};
} else {
map[arr[i].name].value += arr[i].value;
map[arr[i].name].count++;
}
var newArr = [];
for(prop in map)
{
map[prop].value /= map[prop].count;
newArr.push({
name: prop,
value: map[prop].value
});
}
delete map;

Add new property to existing array of object from another array

I don't know how to write the title properly, pardon me on that.
Basically I have a list of array of object that's coming from a place, I need to map them together. How how with my code below I can't make it.
const person = [
{name:'hello',id:1},
{name:'javascript',id:2},
{name:'world',id:3}
];
const selected = [2,3];
const normalized = person.map((obj,i) => obj.id === selected[i] ? Object.assign({}, obj, {checked:true}) : obj);
console.log(normalized)
https://jsfiddle.net/q9g0kazx/1/
I need to add an extra property base on the selected array. Why above code doesn't work?
If I understand you correctly, just iterate through the array using forEach and add the property if needed.
const person = [
{name: 'hello', id: 1},
{name: 'javascript',id: 2},
{name: 'world',id: 3}
];
const selected = [2,3];
person.forEach(p => {
if (selected.includes(p.id)) {
p.checked = true;
}
});
console.log(person);
Or you can use map like this:
const person = [
{name: 'hello', id: 1},
{name: 'javascript',id: 2},
{name: 'world',id: 3}
];
const selected = [2,3];
person.map(p => {
if (selected.includes(p.id)) {
p.checked = true;
}
return p;
});
console.log(person);
Notice that you have to return the object (person in our case)
You can do this:
Check if the the id in the array is present in the selected array by:
selected.includes(obj.id)
So, includes returns true if the obj.id was present in the selected array. If present(yes) then your Object.assignpart of code executes.
The reason your code was not working was because your person array and selected array don't have same number of elements(count) and perhaps not in the order as well.
So person[0] id which is 1 doesn't match with selected[0] id which 2 and so on.
const person = [{
name: 'hello',
id: 1
},
{
name: 'javascript',
id: 2
},
{
name: 'world',
id: 3
}
];
const selected = [2, 3];
const normalized = person.map((obj, i) => selected.includes(obj.id) ? Object.assign({}, obj, {
checked: true
}) : obj);
console.log(normalized);

How can I use lodash/underscore to sort by multiple nested fields?

I want to do something like this:
var data = [
{
sortData: {a: 'a', b: 2}
},
{
sortData: {a: 'a', b: 1}
},
{
sortData: {a: 'b', b: 5}
},
{
sortData: {a: 'a', b: 3}
}
];
data = _.sortBy(data, ["sortData.a", "sortData.b"]);
_.map(data, function(element) {console.log(element.sortData.a + " " + element.sortData.b);});
And have it output this:
"a 1"
"a 2"
"a 3"
"b 5"
Unfortunately, this doesn't work and the array remains sorted in its original form. This would work if the fields weren't nested inside the sortData. How can I use lodash/underscore to sort an array of objects by more than one nested field?
I've turned this into a lodash feature request: https://github.com/lodash/lodash/issues/581
Update: See the comments below, this is not a good solution in most cases.
Someone kindly answered in the issue I created. Here's his answer, inlined:
_.sortBy(data, function(item) {
return [item.sortData.a, item.sortData.b];
});
I didn't realize that you're allowed to return an array from that function. The documentation doesn't mention that.
If you need to specify the sort direction, you can use _.orderBy with the array of functions syntax from Lodash 4.x:
_.orderBy(data, [
function (item) { return item.sortData.a; },
function (item) { return item.sortData.b; }
], ["asc", "desc"]);
This will sort first ascending by property a, and for objects that have the same value for property a, will sort them descending by property b.
It works as expected when the a and b properties have different types.
Here is a jsbin example using this syntax.
There is a _.sortByAll method in lodash version 3:
https://github.com/lodash/lodash/blob/3.10.1/doc/README.md#_sortbyallcollection-iteratees
Lodash version 4, it has been unified:
https://lodash.com/docs#sortBy
Other option would be to sort values yourself:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
function compareValues(v1, v2) {
return (v1 > v2)
? 1
: (v1 < v2 ? -1 : 0);
};
var data = [
{ a: 2, b: 1 },
{ a: 2, b: 2 },
{ a: 1, b: 3 }
];
data.sort(function (x, y) {
var result = compareValues(x.a, y.a);
return result === 0
? compareValues(x.b, y.b)
: result;
});
// data after sort:
// [
// { a: 1, b: 3 },
// { a: 2, b: 1 },
// { a: 2, b: 2 }
// ];
The awesome, simple way is:
_.sortBy(data, [function(item) {
return item.sortData.a;
}, function(item) {
return item.sortData.b;
}]);
I found it from check the source code of lodash, it always check the function one by one.
Hope that help.
With ES6 easy syntax and lodash
sortBy(item.sortData, (item) => (-item.a), (item) => (-item.b))
I think this could work in most cases with underscore:
var properties = ["sortData.a", "sortData.b"];
data = _.sortBy(data, function (d) {
var predicate = '';
for (var i = 0; i < properties.length; i++)
{
predicate += (i == properties.length - 1
? 'd.' + properties[i]
: 'd.' + properties[i] + ' + ')
}
return eval(predicate)
});
It works and you can see it in Plunker
If the problem is an integer is converted to a string, add zeroes before the integer to make it have the same length as the longest in the collection:
var maxLength = _.reduce(data, function(result, item) {
var bString = _.toString(item.sortData.b);
return result > bString.length ? result : bString.length;
}, 0);
_.sortBy(data, function(item) {
var bString = _.toString(item.sortData.b);
if(maxLength > bString.length) {
bString = [new Array(maxLength - bString.length + 1).join('0'), bString].join('');
}
return [item.sortData.a, bString];
});
I've found a good way to sort array by multiple nested fields.
const array = [
{id: '1', name: 'test', properties: { prop1: 'prop', prop2: 'prop'}},
{id: '2', name: 'test2', properties: { prop1: 'prop second', prop2: 'prop second'}}
]
I suggest to use 'sorters' object which will describe a key and sort order. It's comfortable to use it with some data table.
const sorters = {
'id': 'asc',
'properties_prop1': 'desc',//I'm describing nested fields with '_' symbol
}
dataSorted = orderBy(array, Object.keys(sorters).map(sorter => {
return (row) => {
if (sorter.includes('_')) { //checking for nested field
const value = row["properties"][sorter.split('_')[1]];
return value || null;
};
return row[sorter] || null;// checking for empty values
};
}), Object.values(sorters));
This function will sort an array with multiple nested fields, for the first arguments it takes an array to modify, seconds one it's actually an array of functions, each function have argument that actually an object from 'array' and return a value or null for sorting. Last argument of this function is 'sorting orders', each 'order' links with functions array by index. How the function looks like simple example after mapping:
orderBy(array, [(row) => row[key] || null, (row) => row[key] || null , (row) => row[key] || null] , ['asc', 'desc', 'asc'])
P.S. This code can be improved, but I would like to keep it like this for better understanding.

Categories