Categorize object in JavaScript loop - javascript

I have the following object:
var object = [
{
"category": "Apple",
"color": "green"
},
{
"category": "Orange",
"color": "Orange"
},
{
"category": "Apple",
"color": "green"
}
];
I am trying to iterate the data via category, so the following would show in a list:
Apple
Apple
Orange
Below is the code I've tried but unfortuantely it shows in the order it appears in the object. Any ideas would be much appreciated.
function makeUL(object) {
var list = document.createElement('ul');
for(var i = 0; i < object.length; i++) {
var item = document.createElement('li');
var a = document.createElement("a");
a.textContent = object[i].category;
a.setAttribute('href', 'http://test');
item.appendChild(a);
list.appendChild(item);
}
return list;
}

A solution with group change:
var object = [{ "category": "Apple", "color": "green" }, { "category": "Orange", "color": "Orange" }, { "category": "Apple", "color": "green" }];
function makeUL(object) {
var div = document.createElement('div');
object.sort(function (a, b) { return a.category.localeCompare(b.category); });
object.forEach(function (aa) {
var a = document.createElement("a"),
item = document.createElement('li'),
p;
if (aa.category !== this.last) {
p = document.createElement('p');
p.innerHTML = aa.category;
div.appendChild(p);
this.list = document.createElement('ul');
div.appendChild(this.list);
this.last = aa.category;
}
a.textContent = aa.category;
a.setAttribute('href', 'http://test');
item.appendChild(a);
this.list.appendChild(item);
}, { last: undefined, list: undefined });
return div;
}
document.body.appendChild(makeUL(object));

You could use Array.sort
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
function compare(a, b) {
if (a is less than b by some ordering criterion) {
return -1;
}
if (a is greater than b by the ordering criterion) {
return 1;
}
// a must be equal to b
return 0;
}

sort the array before using it in for loop
object.sort(function(a,b){
return a.category > b.category
});

object.sort(function(a, b) {
return a.category > b.category;
});
Result
[ { category: 'Apple', color: 'green' },
{ category: 'Apple', color: 'green' },
{ category: 'Orange', color: 'Orange' } ]

If you want your array to be sorted alphabetically ( DESC order, from A to Z ) you have to call the .sort() method first
function makeUL(object) {
var list = document.createElement('ul');
object.sort();
for(var i = 0; i < object.length; i++) {
var item = document.createElement('li');
var a = document.createElement("a");
while(i<length - 1 && object[i].category == object[i+1].category)
a.textContent += object[i].color + ", ";
a.setAttribute('href', 'http://test');
item.appendChild(a);
list.appendChild(item);
}
return list;
}

Related

How can I check if object exists in array of objects and update quantity?

I have an array objArray. I want to make a function so that it will check if there is another object with the same name key. If it exists, it will add +1 in the qty key. If the object doesn't exist, it will push the new object to the array.
var objArray = [
{"name":"bike","color":"blue","qty":2},
{"name":"boat","color":"pink", "qty":1},
];
var carObj = {"name":"car","color":"red","qty":1};
var bikeObj = {"name":"bike","color":"blue","qty":1};
function checkAndAdd (obj) {
for (var i = 0; i < objArray.length; i++) {
if (objArray[i].name === obj.name) {
objArray[i].qty++;
break;
}
else {
objArray.push(obj);
}
};
}
checkAndAdd(carObj);
console.log(objArray);
checkAndAdd(bikeObj);
console.log(objArray);
After checkAndAdd(carObj);
console.log(objArray);
Should give
[
{"name":"car","color":"red", "qty":1},
{"name":"bike","color":"blue","qty":2},
{"name":"boat","color":"pink", "qty":1},
]
And fter checkAndAdd(bikeObj);
console.log(objArray);
Should give
[
{"name":"car","color":"red", "qty":1},
{"name":"bike","color":"blue","qty":3},
{"name":"boat","color":"pink", "qty":1},
]
Thanks in advance!
You need to check all objects and exit the function if one item is found for incrementing the quantity.
If not found push the object.
var objArray = [{ name: "bike", color: "blue", qty: 2 }, { name: "boat", color: "pink", qty: 1 }],
carObj = { name: "car", color: "red", qty: 1 },
bikeObj = { name: "bike", color: "blue", qty: 1 };
function checkAndAdd (obj) {
for (var i = 0; i < objArray.length; i++) {
if (objArray[i].name === obj.name) {
objArray[i].qty++;
return; // exit loop and function
}
}
objArray.push(obj);
}
checkAndAdd(carObj);
console.log(objArray);
checkAndAdd(bikeObj);
console.log(objArray);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The problem is that you're push()ing inside the loop, instead of returning immediately once found and only call push() outside the loop.
A cleaner approach would be:
function checkAndAdd(obj) {
var matchingObj = objArray.find(o => o.name === obj.name);
if (matchingObj)
matchingObj.qty++;
else
objArray.push(obj);
}
You can also use find to search for the object property.
Using Object.assign before pushing the object will clone the object and will not change the original object when you change the qty (If you add more object with the same name.)
var objArray = [
{"name":"bike","color":"blue","qty":2},
{"name":"boat","color":"pink", "qty":1},
];
var carObj = {"name":"car","color":"red","qty":1};
var bikeObj = {"name":"bike","color":"blue","qty":1};
function checkAndAdd(obj) {
let o = objArray.find(o => o.name === obj.name); //Find if name exist
if (!o) objArray.push(Object.assign({},obj)); //If not exist, push.
else o.qty += obj.qty; //If exist, add the qty
}
checkAndAdd(carObj);
console.log(objArray);
checkAndAdd(bikeObj);
console.log(objArray);
Use findIndex to find in objArray if the object exist.If it does then update the object else push the new object
var objArray = [{
"name": "bike",
"color": "blue",
"qty": 2
},
{
"name": "boat",
"color": "pink",
"qty": 1
},
];
var carObj = {
"name": "car",
"color": "red",
"qty": 1
};
var bikeObj = {
"name": "bike",
"color": "blue",
"qty": 1
};
function checkAndAdd(obj) {
var x = objArray.findIndex(function(item) {
return item.name === obj.name;
});
if (x === -1) {
objArray.push(obj)
} else {
objArray[x].qty = objArray[x].qty + obj.qty
}
}
checkAndAdd(carObj);
checkAndAdd(bikeObj);
console.log(objArray);
Simple enough:
function checkAndAdd(obj) {
for (var i=0; i < objArray.length; i++) {
if (objArray[i]name === obj.name) {
objArray[i].qty++;
return
}
}
objArray.push(obj)
}
Explanation:
Your function was pushing each time the object name didn't match. Instead, when this function a matching name, it incrementes the qty and stops and then if the loops ends without any match, it pushes the obj.

Create a json array dynamically

I have an array of objects like this below,
var A = [
{
"111": ["A", "B", "C"]
},
{
"222": ["D", "E", "F"]
}
];
I would like to create a new array dynamically in the format as shown below using js or jQuery.
key in array A should be mapping to attribute text of AA and value should be to children as given below
var AA = [
{
"text": "111",
"state": "open",
"children": [
{
"text": "A"
},
{
"text": "B"
},
{
"text": "C"
}]
},
{
"text": "222",
"state": "open",
"children": [
{
"text": "D"
},
{
"text": "E"
},
{
"text": "F"
}]
}];
How can I accomplish this ? Any thoughts would be helpful
Thanks for all of your suggestions and help.
But Right now I would like to change variable A and input in the format below,
how can i accomplish the same as before.
var A = {"1":["1_1","1_2","1_3"],
"2":["2_1","2_2"],
"3":["3_1"],
"4":["4_1"],
"5":["5_1","5_2"]};
You can map A to AA using Array.prototype.map() (twice).
var AA = A.map(function(item) {
var key = Object.keys(item)[0]; // this will be reliable only because each item has one property.
return {
'text': key,
'state': 'open',
'children': item[key].map(function(child) {
return { 'text': child };
})
};
});
fiddle
My answer is too long, but its still understandable
var A = [
{"111":["A","B","C"]},
{"222":["D","E","F"]}
];
console.log(A);
A = superGenerator(A);
console.log(A);
function superGenerator(data){
var dataF = [];
var children = [];
for (var i = data.length - 1; i >= 0; i--) {
children[i] = [];
for (var [key, val] of iterate_object(data[i])) {
for (var j = val.length - 1; j >= 0; j--) {
children[i][j] = [];
children[i][j] = {
text : val[j]
}
};
}
dataF[i] = {
text : Object.keys(data[i])[0],
state : 'open',
children : children[i]
}
};
return dataF;
}
function* iterate_object(o) {
var keys = Object.keys(o);
for (var i=0; i<keys.length; i++) {
yield [keys[i], o[keys[i]]];
}
}
You can try it by click Run,
Cheers
Try this :
var A = [
{
"111": ["A", "B", "C"]
},
{
"222": ["D", "E", "F"]
}
];
var AA = A.map(item => {
return {
"text": Object.keys(item)[0],
"state": "open",
"children": item[Object.keys(item)[0]].map(elem => { return {"text": elem} })
}
});
console.log(AA);
You can use array#map to iterate through your array, for each object, iterate all its keys and generate the corresponding object.
var arr = [ { "111": ["A", "B", "C"] }, { "222": ["D", "E", "F"] } ],
result = arr.map(o => Object.keys(o).reduce((r,k) => {
return {
text : k,
state : 'open',
children : o[k].map(text => ({text}))
};
},{}));
console.log(result);

Single variable to stand in for multiple key names - javascript

I have an array, something like this:
array =
[
{
"type": "apple",
"color": "red",
"id": "redApple"
},
{
"type": "grape",
"color": "green",
"id": "greenGrape",
"options": [
{
"bunchName": "bunch1",
"size": "8"
},
{
"bunchName": "bunch2",
"size": "10"
},
{
"bunchName": "bunch3",
"size": "5"
}
]
}
]
I have a function that searches for values in the array.
function findValue (index, key) {
return array[index][key];
}
var value = findValue(0, "id");
// returns redApple
Is there a way I could pass a single argument to the function if I wanted to find something deeper in the array? For example, if I wanted to find "bunchName" could I pass it something like 1, "options[0].bunchName" and get back "bunch1"?
I want a function that can handle multiple keys. In my real project sometimes I'm looking for something on the first level, sometimes I'm looking on the second level, sometimes the third level, etc.
jQuery can be used if for some reason that would help.
You could take the string, replace the brackets, split the string and reduce the path for the result. The function uses a default object for missing or not given properties.
function getValue(object, path) {
return path
.replace(/\[/g, '.')
.replace(/\]/g, '')
.split('.')
.reduce(function (o, k) { return (o || {})[k]; }, object);
}
function findValue(index, path) {
return getValue(array[index], path);
}
var array = [{ type: "apple", color: "red", id: "redApple" }, { type: "grape", color: "green", id: "greenGrape", options: [{ bunchName: "bunch1", size: "8" }, { bunchName: "bunch2", size: "10" }, { bunchName: "bunch3", size: "5" }] }];
console.log(findValue(1, "options[0].bunchName"));
From what I understand, output of findValue(object, "bunchName"); should be "bunch3", where object is array in OP's example.
var object =
[
{
"type": "apple",
"color": "red",
"id": "redApple"
},
{
"type": "grape",
"color": "green",
"id": "greenGrape",
"options": [
{
"bunchName": "bunch1",
"size": "8"
},
{
"bunchName": "bunch2",
"size": "10"
},
{
"bunchName": "bunch3",
"size": "5"
}
]
}
]
var findValue = (object, key) => {
var resultValue;
var rec = (currentObj) => {
if(currentObj && typeof currentObj === "object"){
for(let curKey in currentObj){
if (curKey === key){
resultValue = currentObj[curKey];
}else{
rec(currentObj[curKey]);
}
}
}
}
rec(object);
return resultValue;
}
console.log(findValue(object, "bunchName"));
You could add a function that takes an object and a key and returns object[key] and then split your key string into a list of individual keys by the dot. Then you could traverse the list of keys and use the function to get the value for each level in your object:
Totally untested code I just whipped up:
function valueByKey(obj, key) {
if (obj) {
return obj[key];
}
}
function findValue(index, key) {
const keys = key.split('.');
let value = array[index];
for (let i = 0; i < keys.length; i++) {
value = valueByKey(value, keys[i]);
}
return value;
}
Non-recurrent solution:
var array = [
{
'a': {
'b': 1
}
}
];
function findValue(index, key) {
var keys = key.split('.');
var tmp = array[index];
for (var i = 0; i < keys.length; i++) {
if (!tmp.hasOwnProperty(keys[i]) || typeof tmp !== 'object') {
// throw an exception, or return default value – property not found.
}
tmp = tmp[keys[i]];
}
return tmp;
}
findValue(0, 'a.b');

check to see if all objects in an array has a common property

I have an array of objects which I am trying to loop over and check for a common key if it exists for all objects. if the specific key does not exist for all objects I return false.
Here is my code
var x = [{
"item": "alpha",
"value": "red"
}, {
"item": "beta",
"value": "blue"
}, {
"item": "beta",
"value": "gama"
}]
function test(obj) {
var count = 0;
var out = false;
for (var i = 0; i < obj.length; i++) {
if (obj[i].hasOwnProperty('value')) {
count = i;
}
}
if (count == obj.length) {
out = true
}
}
console.log(test(x))
I am getting undefined. Cant figure out what am I missing here
A really simple way to do this is to use Array#every like this
var x = [{
"item": "alpha",
"value": "red"
}, {
"item": "beta",
"value": "blue"
}, {
"item": "beta",
"value": "gama"
}]
function test(obj) {
return obj.every(a => a.hasOwnProperty("value"));
}
console.log(test(x))
Update
As rightfully mentioned by this comment first.
Here can be the simple solution for this object:
var x = [{
"item": "alpha",
"value": "red"
}, {
"item": "beta",
"value": "blue"
}, {
"item": "beta",
"value": "gama"
}];
function test(obj) {
var keyCount = 0;
obj.forEach(function (item, index) {
item.hasOwnProperty('value') && ++keyCount;
});
return keyCount == obj.length;
}
console.log(test(x));
Here is my implementation, which finds every matching key, even nested keys, given a set of objects:
function recurse_obj(obj, cb, _stack = []) {
for (var k in obj) {
cb(k, obj[k], _stack);
if (obj.hasOwnProperty(k) && (obj[k] instanceof Object)) {
_stack.push(k);
recurse_obj(obj[k], cb, _stack);
_stack.pop();
}
}
}
function obj_all_keys(obj) {
var tmp = [];
recurse_obj(obj, (k, v, stack) => {
var ext = (stack.length) ? "." : "";
tmp.push(stack.join(".").concat(ext, k));
});
return tmp;
}
function key_intersection(...objs) {
var lookup = {};
objs.forEach(o => {
obj_all_keys(o).forEach(k => {
if (k in lookup === false)
lookup[k] = 0;
lookup[k]++;
});
});
for (var k in lookup)
if (lookup[k] !== objs.length)
delete lookup[k];
return lookup;
}
Here is the calling code:
var me = { name: { first: "rafael", last: "cepeda" }, age: 23, meta: { nested: { foo: { bar: "hi" } } } };
console.log(key_intersection(me, { name: { first: "hi" } }));
Output: { name: 2, 'name.first': 2 }
The object returned includes only the keys that are found in all the objects, the set intersection, the counts are from book-keeping, and not removed in the callee for performance reasons, callers can do that if need be.
Keys that are included in other nested keys could be excluded from the list, because their inclusion is implied, but I left them there for thoroughness.
Passing a collection (array of objects) is trivial:
key_intersection.apply(this, collection);
or the es6 syntax:
key_intersection(...collection);

How to delete an object from the nested Array using underscore JS

I have nested array's and need to delete the object based on condition.
Array:
grouplist: [
{
name: "one",
optionlist: [
{
optionitem: "green"
},
{
optionitem: "red"
}
]
},
{
name: "two",
optionlist: [
{
optionitem: "yellow"
},
{
optionitem: "red"
},
{
optionitem: "blue"
}
]
},
{
name: "three",
optionlist: [
{
optionitem: "green"
}
]
},
{
name: "four",
optionlist: [
{
optionitem: "blue"
},
{
optionitem: "red"
}
]
}
];
If the optionItem color is green, then I need to remove it completely from my array object.
This is what I have tried.
var returnedData = _.filter(grouplist, function(n) {
return _.some(n.optionlist, function(option){
return option.optionitem!= "green";
});
});
var returnedData = _.filter(grouplist, function(n){
var containsGreen = _.some(n.optionlist, function(option){
return option.optionitem === "green";
})
return !containsGreen;
});
var returnedData = _.reject(grouplist, function(n){
return _.some(n.optionlist, function(option){
return option.optionitem === "green";
});
});
The problem with filter or _.filter is that you're creating a new array. If you want to simply remove an object from the existing array without creating a new one, here's a simple vanilla JS way of doing that in the same number of lines of code:
for (var i = 0, l = grouplist.length; i < l; i++) {
var foundGreen = grouplist[i].optionlist.some(function (el) {
return el.optionitem === 'green';
});
if (foundGreen) grouplist.splice(i, 1); i--; l--;
}
DEMO

Categories