Fetch specific array element data using variable [duplicate] - javascript

This question already has answers here:
How can I access and process nested objects, arrays, or JSON?
(31 answers)
Closed 7 years ago.
Is there a way to locate data in an array by using value identifiers?
Example: In the object below, I want to do something similar to:
for(country in data.countries)
{
if(country.name === "GB") {return-value-of-country.Clear;}
}
Object example:
{
"countries":[
{
"name": "GB",
"Rain":" url1 ",
"Clear":" url2 "
}
...
]
}

Unless you're using ES6 or some external library there's not a great way to do it, something like:
var countries = data.countries,
len = countries.length,
match, i, country;
for (i = 0; i < len; ++i) {
country = countries[i];
if (country.name === 'GB') {
match = country;
break;
}
}
if (match) {
console.log(match.Clear);
}

To iterate over array you need for loop with index:
for(var i = 0; i<data.countries.length; ++i) {
if(data.countries[i].name === "GB") {
//your value is data.countries[i].Clear;
}
}

If you are not using a library like underscore.
I would do something like:
var test = {
"countries":[
{
"name": "GB",
"Rain":" url1_gb",
"Clear":" url2_gb"
},
{
"name": "FR",
"Rain":" url1_fr",
"Clear":" url2_fr"
},
{
"name": "DE",
"Rain":" url1_de",
"Clear":" url2_de"
},
]
}
var getClearUrl = function(country) {
for (var i = test.countries.length - 1; i >= 0; i--) {
if (test.countries[i].name === country) {
return test.countries[i].Clear;
break;
}
};
return false;
}
var clearUrlGB = getClearUrl('GB');
console.log(clearUrlGB);

Related

Checking duplicate in an array that contains objects as an array

I want to check if there exists duplicate outputTypeId in the output array object..
Below is the JSON:
$scope.entities= [
{
"input": {
"id": 134
},
"output": [
{
"id": 135,
"outputTypeId": 4
}
]
},
{
"input": {
"id": 134
},
"output": [
{
"id": 135,
"outputTypeId": 7
}
]
},
{
"input": {
"id": 134
},
"output": [
{
"id": 135,
"outputTypeId": 9
}
]
}
]
Below is the code that I tried but its not going inside the condition after execution..
Let outputTypeId be [7] as I'm checking for multiple outputTypeId's,hence an array
$scope.checkForDuplicateOutputs = (outputTypeId) => {
for (var i = 0; i < $scope.entities.length; i++) {
for (var j = i; j < $scope.entities[i].output[j].length; j++) {
if (outputTypeId.contains($scope.entities[i].output[j].outputTypeId)) {
$scope.isDuplicateOutput = true;
break;
} else {
$scope.isDuplicateOutput = false;
}
}
}
}
function checkForDuplicates(outputTypeIds) {
$scope.isDuplicateOutput = $scope.entities.some(function(entity) { // Loop through entities
return entity.output.some(function(entityOutput) { // Look for any output
return outputTypeIds.indexOf(entityOutput.outputTypeId) != -1; // which is duplicated in 'outputTypeIds'
});
});
}
So this solution uses Array.some - It has a few advantages:
Removes the need to manually break your loops
No need to have i and j variables to keep track of loop counters
No need to duplicate $scope.isDuplicateOutput = <boolean>;
Less lines of code :)
You are breaking only the inner loop with that break statement and the problem is even though the duplicate flag does get set to true, it will be reset to false in the next iteration. Basically, in the end you'll get the result of the last iteration only.
The quickest fix is to use a flag to denote whether the external loop needs to be stopped:
$scope.checkForDuplicateOutputs = (outputTypeId) => {
var breakOut = false; // <--- this is new
for (var i = 0; i < $scope.entities.length; i++) {
if (breakOut) break; // <--- this is new
for (var j = i; j < $scope.entities[i].output[j].length; j++)
if (outputTypeId.contains($scope.entities[i].output[j].outputTypeId)) {
$scope.isDuplicateOutput = true;
breakOut = true; // <--- this is new
break;
} else {
$scope.isDuplicateOutput = false;
}
}
}
}
If you still want to iterate all the entities and have a list of all the duplicates, you can make $scope.isDuplicateOutput an array and just push the duplicate ids into it.

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.

Get the values by giving key order as a string path JSON

Is there a way to get the value of
{
"first": "first-string",
"second-array": [
{
"first-Obj": 2
}
]
}
is there a way to get or update the values by using a path like string
ex: to change the first-obj's value to 1000
changeTheValueAt('second-array/0/firstObj', 1000)
is there any function like above changeTheValueAt or a method.
Use the following function for that -
function changeTheValueAt(obj, path, value) {
var parts = path.split("/"),
final = obj[parts[0]],
i;
for (i = 1; i < parts.length; i++) {
if (i + 1 < parts.length) {
final = final[parts[i]];
}
else {
final[parts[i]] = value;
}
}
}
and then call it like this -
var ob = {
"first": "first-string",
"second-array": [
{
"first-Obj": 2
}
]
};
changeTheValueAt(ob, "second-array/0/first-Obj", 1000)
alert(ob["second-array"][0]["first-Obj"]);
JSFiddle Demo.
Because second-array is an array, you can't access it by name. You can access it by index or you can iterate over and find a value and change it when found.
var yourObj = {
"first": "first-string",
"second-array": [
{
"first-Obj": 2
}
]
};
yourObject.second-array[0].first-Obj = 1000;
You could do something like:
var changeFirstObj = function(node1, node2, new val){
yourObj[node1].forEach(o, i){
for(var prop in o){
if(prop == node2){
o[node2]=newVal;
}
};
changeFirstObj('second-array', 'first-Obj', 1000);
But that's not really very flexible.

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;
}

Removing duplicate element in an array [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Easiest way to find duplicate values in a JavaScript array
Javascript array sort and unique
I have the following array
var output = new array(7);
output[0]="Rose";
output[1]="India";
output[2]="Technologies";
output[3]="Rose";
output[4]="Ltd";
output[5]="India";
output[6]="Rose";
how can i remove the duplicate elements in above array.Is there any methods to do it?
You can write a function like this
function eliminateDuplicates(arr) {
var i,
len=arr.length,
out=[],
obj={};
for (i=0;i<len;i++) {
obj[arr[i]]=0;
}
for (i in obj) {
out.push(i);
}
return out;
}`
Check this here
Maybe more complex than you need but:
function array_unique (inputArr) {
// Removes duplicate values from array
var key = '',
tmp_arr2 = {},
val = '';
var __array_search = function (needle, haystack) {
var fkey = '';
for (fkey in haystack) {
if (haystack.hasOwnProperty(fkey)) {
if ((haystack[fkey] + '') === (needle + '')) {
return fkey;
}
}
}
return false;
};
for (key in inputArr) {
if (inputArr.hasOwnProperty(key)) {
val = inputArr[key];
if (false === __array_search(val, tmp_arr2)) {
tmp_arr2[key] = val;
}
}
}
return tmp_arr2;
}
Code taken from: http://phpjs.org/functions/array_unique:346
You can remove dups from an array by using a temporary hash table (using a javascript object) to keep track of which images you've already seen in the array. This works for array values that can be uniquely represented as a string (strings or numbers mostly), but not for objects.
function removeDups(array) {
var index = {};
// traverse array from end to start
// so removing the current item from the array
// doesn't mess up the traversal
for (var i = array.length - 1; i >= 0; i--) {
if (array[i] in index) {
// remove this item
array.splice(i, 1);
} else {
// add this value to index
index[array[i]] = true;
}
}
}
Here's a working example: http://jsfiddle.net/jfriend00/sVT7g/
For sizable arrays, using an object as a temporary index will be many times faster than a linear search of the array.
First of all, you'll want to use the array literal (var output = []) to declare your array. Second, you'll want to loop through your array and store all the values in a second array. If any value in the first array matches a value in the second array, delete it and continue looping.
Your code would look like this:
var output = [
"Rose",
"India",
"Technologies",
"Rose",
"Ltd",
"India",
"Rose"
]
var doubledOutput = [];
for(var i = 0; i < output.length; i++) {
var valueIsInArray = false;
for(var j = 0; j < doubledOutput.length; j++) {
if(doubledOutput[j] == output[i]) {
valueIsInArray = true;
}
}
if(valueIsInArray) {
output.splice(i--, 1);
} else {
doubledOutput.push(output[i]);
}
}
Please note, the above code is untested and may contain errors.

Categories