How to compare 2 Arrays to update key value pairs in one? - javascript

I have a main array of tags
var tagsArray = [
{
name: "1",
selected: undefined
},
{
name: "2",
selected: undefined
},
{
name: "3",
selected: undefined
}
]
Then an array of selected tags to compare it to:
var selectedTags = [
{
name: "1",
selected: true
}
]
How would you run some kind of comparison (for-loop) to check what objects in selectedTags have the selected: true value?
Then also set the same object's selected value in tagsArray to true?

Create an access map, then iterate and find the true values and set the values in the other array
var map = {};
tagsArray.forEach(function(obj, index) {
map[obj.name] = index;
});
selectedTags.forEach(function(obj) {
if ( obj.selected ) {
tagsArray[map[obj.name]].selected = true;
}
});
FIDDLE

You can still use 2 for-loops and it doesn't look much worse in terms of code or clarity. But on average it will run slower O(n^2) than using map.
var i,j;
for(i = 0; i < tagsArray.length; i++){
for(j = 0; j < selectedTags.length; j++){
if(tagsArray[i].name === selectedTags[j].name){
tagsArray[i].selected = true;
break;
}
}
}
JS Fiddle

Related

how to count children in object in javascript?

I have a json array ..I want to refactor the json object ..but I am not able to make as expected output .I want to search all item or object and insert the children of parent ..I try like that
https://jsfiddle.net/nk6v0y36/1/
var new_Arry = [];
for (var i = 0; i < arry.length; i++) {
var obj = {};
var childeren = [];
obj.con_id = arry[i].con_id;
obj.children = [{
con_to: arry[i].con_to
}];
new_Arry.push(obj)
}
console.log(new_Arry)
Expected output:
[{
"con_id": 11,
"children": [{
"con_to": 12
}]
}, {
"con_id": 12,
"children": [{
"con_to": 13
}, {
"con_to": 14
}]
}, {
"con_id": 13,
"children": []
}, {
"con_id": 14,
"children": [{
"con_to": 15
}]
}, {
"con_id": 15,
"children": [{
"con_to": 16
}]
}, {
"con_id": 16,
"children": []
}]
After answer
https://jsfiddle.net/nk6v0y36/7/
Answer not working..
As thisOneGuy said, you are not checking if a con_id already exists in the new array, so for each element in the old array you are creating a corresponding new one. Your expected output though shows you want to merge these multiple con_id values into a single object.
You are also creating objects with con_to properties in the children arrays even when there is no such property, which again your expected output suggests you do not way.
One way to fix this would be:
var new_Arry = [];
var new_Hash = [];
for (var i = 0; i < arry.length; i++) {
if (new_Hash[arry[i].con_id] === undefined ) {
var obj = {
con_id: arry[i].con_id,
children: []
};
if( arry[i].con_to !== undefined ) {
obj.children.push({ con_to: arry[i].con_to });
}
new_Hash[obj.con_id] = new_Arry.push(obj) - 1;
} else if( arry[i].con_to !== undefined ) {
new_Arry[new_Hash[arry[i].con_id]].children.push({ con_to: arry[i].con_to });
}
}
console.log(new_Arry);
The key here is the new_Hash array, which uses the con_id value as its key, and stores the respective index in the new_Arry as its value.
So for each con_id in arry it first checks the new_Hash to see if there is an entry for it. If not then there is no entry in new_Arry, so it creates a new one.
Firstly the basic object, with con_id and a children array is created. It then checks whether there is a con_to property in arry, and only if there is does it add one as an object to the children array.
This object is then added to new_Arry and new_Hash updated so that an element for con_id references the new new_Arry index. As the .push() method returns extends an array and returns the new array length, the new index will always be this value minus one.
However if an entry for con_id was found in new_Hash then all you need to do it add the con_to value to the existing children array. Whilst presumably if there are multiple con_id entries in arry they must contain a con_to value, it also checks this just to be safe. The index number for the new_Arry as returned by new_Hash[arry[i].cond_id].
Incidentally, the reason for explicitly checking for undefined rather than seeing if value equates to true or false to check for its existence is a con_id or con_to value of 0 would be treated as false and skipped.
Like this -
var new_Arry = [];
for (var i = 0; i < arry.length; i++) {
var obj = {};
var childeren = [];
obj.con_id = arry[i].con_id;
var child = [];
var childrenObj = {};
childrenObj.con_to = arry[i].con_to;
child.push(childrenObj);
obj.children = child;
new_Arry.push(obj)
}
https://jsfiddle.net/nk6v0y36/3/
Here is the correct answer with the correct output : https://jsfiddle.net/nk6v0y36/4/
Basically you need to loop through your array to check if the con_id exists in that array, if it does, just add to children, if it doesnt add a new object.
var new_Arry = [];
for (var i = 0; i < arry.length; i++) {
var obj = {};
var childeren = [];
if (new_Arry.length > 0) {
console.log('start')
console.log(new_Arry.length);
var thisBool = false;
for (var j = 0; j <= new_Arry.length; j++) {
console.log('inside')
if (new_Arry[j]) {
if (new_Arry[j].con_id === arry[i].con_id) {
console.log('has children')
new_Arry[j].children[new_Arry[j].children.length] = {
con_to: arry[i].con_to
}
} else {
thisBool = true;
}
}
}
if(thisBool){
obj.con_id = arry[i].con_id;
obj.children = [{
con_to: arry[i].con_to
}];
new_Arry.push(obj)
}
} else {
console.log('no children')
obj.con_id = arry[i].con_id;
obj.children = [{
con_to: arry[i].con_to
}];
new_Arry.push(obj)
}
}
Check the output in the console log to see it's correct :)

Pushing to array

I need to loop through array and each array in array that has extra values, push them to their parent array as separate item. I hope this makes sense..
This is the structure of my initial array:
{type:
[ 0:
value: "tomato"
],
[ 1:
{value: "apple",
[ extras:
[ 0: { value: "green" } ],
[ 1: { value: "red" } ]
]
],
[ 2:
value: "pineapple"
]
}
What the result would have to look like:
[type:
[ 0:
tomato
],
[ 1:
apple,
green,
red
],
[ 2:
pineapple
]
]
What I've tried and failed: (I also commented the error I get on right line)
var response = /* json of first codeblock in question is response from ajax */;
var items = JSON.parse( response );
var type = Object.keys( items )[0];
var myArray = []
var count = items[type].lenght;
//Loop through main items in "type"
for( i = 0; i < count; i++ ) {
var value = items[type][i][value];
myArray[type][i] = [value]; //Uncaught TypeError: Cannot set property '0' of undefined
if( items[type][i][extras] ) {
var extracount = items[type][i][extras].lenght;
//Loop through extras
for( k = 0; k < extracount; k++ ) {
var extra = items[type][i][extras][k][value];
myArray[type][i].push( extra );
}
}
}
My main problem that I don't understand and that seems to be the problem in my example as well:
If I declare an empty array, how do I:
push an item to that array also declaring a new array around that item?
push another item to that array that was made around the first item?
This is what I believe you want. The following code may be incorrect, because I'm approximating what I believe your items object contains.
var items = {
type: [
{
value: "tomato"
},
{
value: "apple",
extras: [
{
value: "green"
}, {
value: "red"
}
]
},
{
value: "pineapple"
}
]
};
var myArray = {
type: []
};
var count = items['type'].length;
//Loop through main items in "type"
for (i = 0; i < count; i++) {
var subarray = [];
subarray.push(items['type'][i]['value']);
if (items['type'][i]['extras']) {
var extracount = items['type'][i]['extras'].length;
//Loop through extras
for (k = 0; k < extracount; k++) {
var extra = items['type'][i]['extras'][k]['value'];
subarray.push(extra);
}
}
myArray['type'].push(subarray);
}
Some notes:
You will definitely need to learn the difference between an array and an object in javascript. There are plenty of resources online for this.
When retrieving/manipulating a property prop from an object obj (i.e. for a key-value pair), you will need to use obj.prop or obj['prop']. Note the use of a string in the latter example.
For an array arr, you should use arr.push(value) to push a new value onto the array.
Your problem is here:
var value = items[type][i][value];
you should change it to
var value = items[type][i].value;

Array of unique JSON values

I'm trying to get an array of unique JSON data based on the comparison of a key value.
In this example, I'm trying to remove any objects with duplicate category values.
Example:
var products = [
{ category: 'fos', name: 'retek' },
{ category: 'fos', name: 'item' },
{ category: 'nyedva', name: 'blabla' },
{ category: 'fos', name: 'gihi' }
];
// array of hold unique values
var uniqueNames = [];
for(i = 0; i< products.length; i++){
if(uniqueNames.indexOf(products[i].category) === -1){
uniqueNames.push(products[i]);
}
}
I'm trying to push to the array any object that doesn't have duplicate category values. Here is a live JSbin.
Please help!
There are several ways to do this, this is one of them: traverse all the items, and filter out the ones which we have already added with that category. For this we use an object to keep which categories we have seen and which ones are new, so we filter only the seen ones:
var seen = {}
var unique = products.filter(function(item){
if(seen.hasOwnProperty(item.category)){
return false;
}else{
seen[item.category] = true;
return true;
}
})
console.log(unique); // only 2 objects
When I am trying to do this, I usually put all of the values into a map as keys, since the map data structure will only allow unique keys. So for this case:
var crops = [ {
id: 0023,
crop: "corn"
},
{
id: 0034,
crop: "corn"
},
{
id: 0222,
crop: "wheat"
}
];
var cropsMap = {};
for(var i = 0; i < crops.length; i++) {
cropsMap[crops[i].crop] = true;
}
var uniqueCrops = Object.keys(cropsMap);
I made a codepen if you want to check it out.
lookup = [];
for (var product, i = 0; product = products[i++];) {
var cat = item.category;
if (!(cat in lookup)) {
lookup[cat] = 1;
result.push(products[cat]);
}
}
Switch
for(i = 0; i< products.length; i++){
if(uniqueNames.indexOf(products[i].category) === -1){
uniqueNames.push(products[i]);
}
}
To
for(i = 0; i< products.length; i++){
if(uniqueNames.indexOf(products[i].category) === -1){
uniqueNames.push(products[i].category); // Push Name of category. Will now not place duplicates into UnqiueNames
}
}
Console
["fos", "nyedva"]

Access by value on nested JSON arrays

I'm new to JavaScript and I'm really lost here. Here is some data produced by PHP json_encode() (and limited to most pertinent keys) :
[
{
"product_option_id":"229",
"product_option_value":
[
{
"product_option_value_id":"21",
"option_value_id":"51",
"price":"1,22 €",
"price_prefix":"+"
},
{
"product_option_value_id":"22",
"option_value_id":"52",
"price":false,
"price_prefix":"+"
},
{
"product_option_value_id":"23",
"option_value_id":"53",
"price":"2,42 €",
"price_prefix":"+"
}
],
"option_id":"14",
"type":"radio",
"value":""
},
{
"product_option_id":"228",
"product_option_value":
[
{
"product_option_value_id":"19",
"option_value_id":"49",
"price":"1,22 €",
"price_prefix":"+"
},
{
"product_option_value_id":"20",
"option_value_id":"50",
"price":"2,42 €",
"price_prefix":"+"
}
],
"option_id":"13",
"type":"select",
"value":""
}
]
I need to access price and price_prefix values (in JavaScript) knowing product_option_id and product_option_value_id.
How do I do that ? Should I go for a loop ?
Update :
Thanks for replies. Unless I missed something, it appears that in my case arrays (as ugly as they may be…) are much more efficient than all the proposed solutions (I'll try another approach, formatting a JSON object corresponding to my needs with PHP rather than using the "default" one, but it's off topic here). Though I'm not fond of adding libraries and it's a bit slower than most other solutions, I'll accept Matt's solution because it really seems to make life easier as far as JSON access is concerned. But it should be noted that Yeldard and Barmar's (almost cloned) solutions are faster than other propositions.
lodash would make this easier and neater. It provides _.find or _.filter depending on if your id's are unique or not.
var record = _.find( data_structure, {
"product_option_id": "229"
})
if ( !record ) throw new Error("Record not found");
var value = _.find( record.product_option_value, {
"product_option_value_id":"22"
})
if ( !value ) throw new Error("Value not found");
console.log( "price[%s] prefix[%s]", value.price, value.price_prefix )
Demo
For more complex data selection, you might want to look at sift.js. It's based on mongodb's query system.
var records = sift({
"product_option_id": "229",
"product_option_value": {
$elemMatch: {
"product_option_value_id": "22"
}
}
},
data_structure
)
you can do like this
for(var i in jsonData) {
var item = jsonData[i];
if(item.product_option_id == 229) {
for(var j in item.product_option_value){
var item1 = item.product_option_value[j];
if(item1.product_option_value_id == 21) {
//your item here
break;
}
}
break;
}
}
This should do it:
var productOptionId = 229;
var productOptionValue = 22;
var matchingOuter = yourData.filter(function(i){
return i.product_option_id === productOptionId;
})[0];
if (matchingOuter) {
var matchingInner = matchingOuter.product_option_value.filter(function(i){
return i.product_option_value === productOptionValue;
})[0];
}
If a matching item exists it will be assigned to matchingInner
Following would do:
function getProductValues(products, product_option_id, product_option_value_id) {
if (!product_option_id || !product_option_value_id) {
return;
}
return products.filter(function(product) {
return +product.product_option_id === product_option_id;
}).map(function (product) {
var option_values = product.product_option_value;
return option_values.filter(function (option) {
return +option.option_value_id === product_option_value_id;
})[0] || [];
})[0] || [];
}
Usage:
getProductValues(data, 229, 51)
Result:
{product_option_value_id: "21", option_value_id: "51", price: "1,22 €", price_prefix: "+"}
Use filter on the main array to grab the right object, filter again on the option_value_id, then map on the returned array to get a single price/prefix object. map and filter both return arrays which is why you see the code picking up the first element ([0]) in a couple of places.
function getData(data, options) {
return data.filter(function (product) {
return product.product_option_id === options.id;
})[0].product_option_value.filter(function (details) {
return details.product_option_value_id === options.optionId;
}).map(function(el) {
return { price: el.price, prefix: el.price_prefix }
})[0];
}
getData(data, { id: '229', optionId: '23' }); // { price: "2,42 €", prefix: "+" }
DEMO
Use nested loops to search through the main array and the sub-arrays, looking for the matching element.
function find_product(product_option_id, product_option_value_id) {
for (var i = 0; i < products.length; i++) {
var product = products[i];
if (product.product_option_id == product_option_id) {
for (var j = 0; j < product.product_option_value.length; j++) {
var value = product.product_option_value[j];
if (value.product_option_value_id == product_option_value_id) {
return { price: value.price, price_prefix: value.price_prefix }
}
}
}
}
}
Yes, you need to enumerate through the array and find your items:
Here is the working code which outputs price_prefix and price of product with product_option_id = 228 and product_option_value_id = 19. You can replace these values with your own.
for (var i = 0; i < obj.length; i++) // Enumerate through array
{
var item = obj[i];
if (item.product_option_id === "228") // Filtering items by product_option_id
{
// When necessary product_option_id found
for (var j = 0; j < item.product_option_value.length; j++) // Enumerate through its products
{
var productItem = item.product_option_value[j];
if (productItem.product_option_value_id === "19") // Filtering by product_option_value_id
{
// here it is. productItem is found! do whatever you want with it
alert(productItem.price_prefix + " " + productItem.price);
}
}
}
}
Working JSFiddle demo.

Remove duplicate objects from an array using javascript

I am trying to figure out an efficient way to remove objects that are duplicates from an array and looking for the most efficient answer. I looked around the internet everything seems to be using primitive data... or not scalable for large arrays. This is my current implementation which is can be improved and want to try to avoid labels.
Test.prototype.unique = function (arr, artist, title, cb) {
console.log(arr.length);
var n, y, x, i, r;
r = [];
o: for (i = 0, n = arr.length; i < n; i++) {
for (x = 0, y = r.length; x < y; x++) {
if (r[x].artist == arr[i].artist && r[x].title == arr[i].title) {
continue o;
}
}
r.push(arr[i]);
}
cb(r);
};
and the array looks something like this:
[{title: sky, artist: jon}, {title: rain, artist: Paul}, ....]
Order does not matter, but if sorting makes it more efficient then I am up for the challenge...
and for people who do not know o is a label and it is just saying jump back to the loop instead of pushing to the new array.
Pure javascript please no libs.
ANSWERS SO FAR:
The Performance Test for the answers below:
http://jsperf.com/remove-duplicates-for-loops
I see, the problem there is that the complexity is squared. There is one trick to do it, it's simply by using "Associative arrays".
You can get the array, loop over it, and add the value of the array as a key to the associative array. Since it doesn't allow duplicated keys, you will automatically get rid of the duplicates.
Since you are looking for title and artist when comparing, you can actually try to use something like:
var arrResult = {};
for (i = 0, n = arr.length; i < n; i++) {
var item = arr[i];
arrResult[ item.title + " - " + item.artist ] = item;
}
Then you just loop the arrResult again, and recreate the array.
var i = 0;
var nonDuplicatedArray = [];
for(var item in arrResult) {
nonDuplicatedArray[i++] = arrResult[item];
}
Updated to include Paul's comment. Thanks!
Here is a solution that works for me.
Helper functions:
// sorts an array of objects according to one field
// call like this: sortObjArray(myArray, "name" );
// it will modify the input array
sortObjArray = function(arr, field) {
arr.sort(
function compare(a,b) {
if (a[field] < b[field])
return -1;
if (a[field] > b[field])
return 1;
return 0;
}
);
}
// call like this: uniqueDishes = removeDuplicatesFromObjArray(dishes, "dishName");
// it will NOT modify the input array
// input array MUST be sorted by the same field (asc or desc doesn't matter)
removeDuplicatesFromObjArray = function(arr, field) {
var u = [];
arr.reduce(function (a, b) {
if (a[field] !== b[field]) u.push(b);
return b;
}, []);
return u;
}
and then simply call:
sortObjArray(dishes, "name");
dishes = removeDuplicatesFromObjArray(dishes, "name");
Basic sort-then-unique implementation, fiddle HERE:
function unique(arr) {
var comparer = function compareObject(a, b) {
if (a.title == b.title) {
if (a.artist < b.artist) {
return -1;
} else if (a.artist > b.artist) {
return 1;
} else {
return 0;
}
} else {
if (a.title < b.title) {
return -1;
} else {
return 1;
}
}
}
arr.sort(comparer);
console.log("Sorted: " + JSON.stringify(arr));
for (var i = 0; i < arr.length - 1; ++i) {
if (comparer(arr[i], arr[i+1]) === 0) {
arr.splice(i, 1);
console.log("Splicing: " + JSON.stringify(arr));
}
}
return arr;
}
It may or may not be the most efficient, and should be entirely scalable. I've added some console.logs so you can see it as it works.
EDIT
In the interest of saving on the space the function used, I did that for loop at the end, but it seems likely that didn't properly find only unique results (depsite it passing my simple jsfiddle test). Please try replacing my for loop with the following:
var checker;
var uniqueResults = [];
for (var i = 0; i < arr.length; ++i) {
if (!checker || comparer(checker, arr[i]) != 0) {
checker = arr[i];
uniqueResults.push(checker);
}
}
return uniqueResults;
I use this function. its not doing any sorting, but produces result. Cant say about performance as never measure it.
var unique = function(a){
var seen = [], result = [];
for(var len = a.length, i = len-1; i >= 0; i--){
if(!seen[a[i]]){
seen[a[i]] = true;
result.push(a[i]);
}
}
return result;
}
var ar = [1,2,3,1,1,1,1,1,"", "","","", "a", "b"];
console.log(unique(ar));// this will produce [1,2,3,"", "a", "b"] all unique elements.
Below is Henrique Feijo's answer with ample explanation and an example that you can cut and paste:
Goal: Convert an array of objects that contains duplicate objects (like this one)...
[
{
"id": 10620,
"name": "Things to Print"
},
{
"id": 10620,
"name": "Things to Print"
},
{
"id": 4334,
"name": "Interesting"
}
]
... Into an array of objects without duplicate objects (like this one):
[
{
"id": 10620,
"name": "Things to Print"
},
{
"id": 4334,
"name": "Interesting"
}
]
Explanation provided in the comments:
var allContent = [{
"id": 10620,
"name": "Things to Print"
}, {
"id": 10620,
"name": "Things to Print"
}, {
"id": 4334,
"name": "Interesting"
}]
//Put Objects Into As Associative Array. Each key consists of a composite value generated by each set of values from the objects in allContent.
var noDupeObj = {} //Create an associative array. It will not accept duplicate keys.
for (i = 0, n = allContent.length; i < n; i++) {
var item = allContent[i]; //Store each object as a variable. This helps with clarity in the next line.
noDupeObj[item.id + "|" + item.name] = item; //This is the critical step.
//Here, you create an object within the associative array that has a key composed of the two values from the original object.
// Use a delimiter to not have foo+bar handled like fo+obar
//Since the associative array will not allow duplicate keys, and the keys are determined by the content, then all duplicate content are removed.
//The value assigned to each key is the original object which is along for the ride and used to reconstruct the list in the next step.
}
//Recontructs the list with only the unique objects left in the doDupeObj associative array
var i = 0;
var nonDuplicatedArray = [];
for (var item in noDupeObj) {
nonDuplicatedArray[i++] = noDupeObj[item]; //Populate the array with the values from the noDupeObj.
}
console.log(nonDuplicatedArray)
For those who love ES6 and short stuff, here it's one solution:
const arr = [
{ title: "sky", artist: "Jon" },
{ title: "rain", artist: "Paul" },
{ title: "sky", artist: "Jon" }
];
Array.from(arr.reduce((a, o) => a.set(o.title, o), new Map()).values());
const arr = [
{ title: "sky", artist: "Jon" },
{ title: "rain", artist: "Paul" },
{ title: "sky", artist: "Jon" },
{ title: "rain", artist: "Jon" },
{ title: "cry", artist: "Jon" }
];
const unique = Array.from(arr.reduce((a, o) => a.set(o.title, o), new Map()).values());
console.log(`New array length: ${unique.length}`)
console.log(unique)
The above example only works for a unique title or id. Basically, it creates a new map for songs with duplicate titles.
Below code compares object with JSON as String format and removes duplicates and works fine with simple arrays.
Array.prototype.unique=function(a){
return function(){
return this.filter(a)
}
}(
function(a,b,c){
var tmp=[];
c.forEach(function(el){
tmp.push(JSON.stringify(el))
});
return tmp.indexOf(JSON.stringify(a),b+1)<0
})
If you are using underscore js, it is easy to remove duplicate object.
http://underscorejs.org/#uniq
function remove_duplicates(objectsArray) {
var arr = [], collection = [];
$.each(objectsArray, function (index, value) {
if ($.inArray(value.id, arr) == -1) {
arr.push(value.id);
collection.push(value);
}
});
return collection;
}

Categories