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"]
Related
I have a function like this: pickListSelect array is has all id (numbers) to delete objects in source array, and target array it is to push elements deleted from source array.
function copy(pickListSelect, source, target) {
var i, id;
for (i = 0; i < pickListSelect.length; i++) {
id = pickListSelect[i];
source.splice(id,1);
}
pickListSelect = [];
}
So what I need is delete specific object from source array. I tried with that code but for example if I need to delete object with id=5, it only deleted item 5 from the list.
The structure of source array is this:
[Object, Object, Object, Object, Object, Object, Object, Object, Object]
0:Object
plantId:1
plantName:"Plant 1"
...the rest of others are similar object
You need to find plant in your source by plantId first, and then delete it from original array and push to target. Open console and it should log deleted plants:
var plants = [
{
plantId: 1,
plantName: 'plant 1'
},
{
plantId: 2,
plantName: 'plant 2'
},
{
plantId: 3,
plantName: 'plant 3'
},
{
plantId: 4,
plantName: 'plant 4'
}
];
function copy(pickListSelect, source, target) {
var i, id, el;
for (i = 0; i < pickListSelect.length; i++) {
id = pickListSelect[i];
el = findPlant(source, id);
source.splice(source.indexOf(el), 1);
target.push(el);
}
}
function findPlant (arr, id) {
return arr.filter(function (plant) {
return plant.plantId == id
})[0]
}
var test = [];
copy([2,3], plants, test);
console.log(test);
When you use .splice you need to pass in the start index at which to splice and the amount of items to splice, try this:
source.splice(i,1); // i is your starting index here
array.splice(start, deleteCount[, item1[, item2[, ...]]])
MDN on .splice
Now in your actual code you need to check to see if the id matches and then splice using the above code:
function copy(pickListSelect, source, target) {
var i, id;
for (i = 0; i < pickListSelect.length; i++) {
if (pickListSelect[i].id === someId) {
source.splice(i,1);
}
}
pickListSelect = [];
}
You can take a look at this fiddler here.
I have used underscore.js to find the correct element from source and move it to the target array.
var copy = function(pickListSelect, source, target) {
for (i = 0; i < pickListSelect.length; i++) {
id = pickListSelect[i];
var deleteIndex = _.findIndex(source, {Id: id});
var deletedItem = source.splice(deleteIndex, 1);
target.push(deletedItem[0])
}
pickListSelect = [];
return target;
}
You're not looking up the index of the source array with a matching id. It might be better to do something like this.
var idsToRemove = {};
// build an object of ids to remove (effectively a hashset)
for (var i = 0; i < pickSelectList.length; i++) {
idsToRemove[pickSelectList[i]] = true;
}
// loop through the source array to find any objects with ids to remove
for (var j = 0; j < source.length; j++) {
if (source[j].plantId in idsToRemove) {
target.push(source.splice(j, 1));
}
}
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
I am trying to build a 3-tiered function:
First, an array lists available workshops (array is called 'workshops').
Second, another array lists workshops that a user has selected (this array is called 'selectedWorkshops').
Third, I have a final array called 'registeredWorkshops'.
When my function is run, I want objects within 'selectedWorkshops' to be added to 'registeredWorkshops', then I want to delete any objects within 'selectedWorkshops' from both 'selectedWorkshops' and any matching elements from 'workshops'. So, where those objects used to exist in both 'selectedworkshops' and 'workshops', now they only exist in 'registeredWorkshops'.
Here's what I've got so far:
addRemoveWorkshops = function(){
var numberOfWorkshops = selectedWorkshops.length;
for(var i = 0; i < numberOfWorkshops; i++ ){
registeredWorkshops.push(selectedWorkshops[i]);
for(var j = 0, arrayLength = workshops.length; j < arrayLength; j++) {
var searchTerm = selectedWorkshops[i].WorkshopId;
if (workshops[j].WorkshopId === searchTerm) {
workshops = workshops.slice(j);
}
}
selectedWorkshops = selectedWorkshops.slice(i);
}
};
addRemoveWorkshops();
However, the function doesn't appear to work properly. It doesn't seem to be deleting the correct workshops, and it only seems to add one of the selectedWorkshops to registeredWorkshops. What am I doing wrong?
Here's a codepen demonstration: http://codepen.io/trueScript/pen/GgVWMx
If it's not possible to add other properties to the objects (as per my other answer) then I'd tackle it like this:
function registration(workshops, selected, registered) {
// add the selected workshops to registered
selected.forEach(function(workshop) {
registered.push(workshop);
});
// remove them from the other lists
registered.forEach(function(workshop) {
removeWorkshop(selected, workshop);
removeWorkshop(workshops, workshop);
});
}
function removeWorkshop(list, workshop) {
var index = list.indexOf(workshop);
if(index >= 0) {
list.splice(index, 1);
}
}
The function expects each of the arrays to be passed in as arguments and it will modify them in place. Things always become clearer and easier to test if you move your loops out into functions before nesting them.
There should be no reason not to use the indexOf method here, as it saves you having to write an extra loop. However, if for some reason you needed to use the WorkshopId property to locate the item within the list, you could create another helper method to do this for you.
function findWorkshop(list, workshop) {
for(var i = 0; i < list.length; i++) {
if(list[i].WorkshopId === workshop.WorkshopID) {
return i;
}
}
return -1;
}
Then you just amend the removeWorkshop function to reflect that.
function removeWorkshop(list, workshop) {
var index = findWorkshop(list, workshop);
list.splice(index, 1);
}
I think it would be easier to slightly rethink your data structure. If you go for the imperative solution above, you run this risk of ending up with duplicate values in more than one list.
Would it not be easier to add registered and selected properties to your workshop objects?
var workshops = [
{
name: 'apples',
WorkshopId: '19',
registered: true,
selected: false
},
{
name: 'oranges',
WorkshopId: '3b',
selected: true,
registered: false
},
// ...
];
Then if you need to be able to get a list of all the registered workshops, you can create it using a filter.
// helper function for filtering based
// on a given property
function property(name) {
return function(object) {
return object[name];
}
}
var registered = workshops.filter(property('registered'));
var selected = workshops.filter(property('selected'));
To select a workshop, all you need to do is change the select property to true:
workshops[3].selected = true;
You could then write the original function to register all workshops that were selected like this:
function registration(workshops) {
workshops.forEach(function(workshop) {
if(workshop.selected) {
workshop.registered = true;
workshop.selected = false;
}
});
}
A while loop + a for one :
var workshops = [{
name: 'apples',
WorkshopId: '19'
}, {
name: 'oranges',
WorkshopId: '3b'
}, {
name: 'pears',
WorkshopId: 'x6'
}, {
name: 'pineapples',
WorkshopId: '55'
}, {
name: 'watermelons',
WorkshopId: '8v'
}];
var selectedWorkshops = [{
name: 'oranges',
WorkshopId: '3b'
}, {
name: 'watermelons',
WorkshopId: '8v'
}, {
name: 'pears',
WorkshopId: 'x6'
}];
var registeredWorkshops = [];
var numberOfWorkshops;
addRemoveWorkshops = function () {
numberOfWorkshops = selectedWorkshops.length;
// A single while statment is enough and lighter
while (selectedWorkshops.length) {
var removedWorkshop;
numberOfWorkshops = registeredWorkshops.push(selectedWorkshops[0]);
for (var i = 0; i < workshops.length; i++)
if (workshops[i].WorkshopId == selectedWorkshops[0].WorkshopId) {
workshops.splice(i, 1);
break;
}
selectedWorkshops.splice(0, 1);
}
};
addRemoveWorkshops();
// Better for viewing the content (in firefox I have just "Object") :
console.log("workshops : ");
for (var i = 0; i < workshops.length; i++)
console.log('- ' + workshops[i].name);
console.log("selectedWorkshops : ");
for (var i = 0; i < selectedWorkshops.length; i++)
console.log('- ' + selectedWorkshops[i].name);
console.log("registeredWorkshops : ");
for (var i = 0; i < registeredWorkshops.length; i++)
console.log('- ' + registeredWorkshops[i].name);
addRemoveWorkshops = function(){
var numberOfWorkshops = selectedWorkshops.length;
for(var i = 0; i < numberOfWorkshops; i++ ){
registeredWorkshops.push(selectedWorkshops[i]);
for(var j = 0, arrayLength = workshops.length; j < arrayLength; j++) {
var searchTerm = selectedWorkshops[i].WorkshopId;
if (workshops[j].WorkshopId === searchTerm) {
workshops = workshops.splice(j,1);
}
}
selectedWorkshops = selectedWorkshops.splice(i,1);
}
};
So I have this array and I want to make a new array with those objects that have the swimming value in sports.
var watchesArray = [
{
model: "Swim",
image:"",
price: 149.99,
sports:["Swimming", "Running"]
},
{
model: "FR 10",
image:"",
price: 129.99,
sports:["Running"]
},
{
model: "FR 15",
image:"",
price: 199.99,
sports:["Running"]
},
];
So far I have this but I dont know how to add on to the sliced array with each go around in the for loop. How should I do this?
for (var i = 0; i < watchesArrayLength; i++) {
if (watchesArray[i].sports.indexOf("Swimming") > -1) {
var runningWatchArray = watchesArray.slice(i);
}
}
You can use .filter() method:
watchesArray = [...];
var result = watchesArray.filter(function(watch) {
return watch.sports.indexOf('Swimming') !== -1;
});
If I understand correctly, what you want is
var runningWatchArray = [];
for (var i = 0; i < watchesArrayLength; i++) {
if (watchesArray[i].sports.indexOf("Swimming") > -1) {
var watch = watchesArray.slice(i);
runningWatchArray.push(watch);
}
}
You could also use forEach to loop through each item of the watchesArray...
var runningWatchArray = new Array();
watchesArray.forEach(function(watch){
if (watch.sports.indexOf("Swimming") > -1) {
runningWatchArray.push(watch);
}
}
I have a master collection of items with unique ID's.
At some point I have a subset of IDs from the master list that belong to some sub grouping if you will. The subset is just a reference of IDs of items that exist in the master list. Is there a way I can ask the master list for just the items that match the IDs in my subset without having to loop through the entire master collection?
Just trying to find the fastest way to do this rather than the standard loop.
//go through master list and determine which items belong to this sub item grouping
for (var item = 0; item < masterListItems.length; ++item ) {
for (var subItem = 0; subItem < subItems.length; ++subItem ) {
if (masterListItems[item].Id == subItems[subItem].Id) { //if it is a sub item
//do some UI specific thing
}
}
}
Here is a solution with jQuery.grep. Filtering in 3 lines :
var master = [{ Id: 3 },{ Id: 1 },{ Id: 2 }]
var ids = [{ Id: 1 },{ Id: 3 }];
$(document).ready(function()
{
// Filtering with 3 lines
idList = [];
$.each(ids,function(index,value) { idList[idList.length] = value.Id; });
elems = $.grep(master,function(element){ return idList.indexOf(element.Id) > -1; });
$.each(elems,function(index,value){
alert(value.Id);
});
});
Edit: Be careful, on Internet Explorer, you will have to define indexOf yourself, as this example :
if(!Array.prototype.indexOf) {
Array.prototype.indexOf = function(needle) {
for(var i = 0; i < this.length; i++) {
if(this[i] === needle) {
return i;
}
}
return -1;
};
}
You can run over the master list once to create "mapping" of the "Id" then one loop over the subset items:
var masterListMapping = new Array();
for (var i = 0; i < masterListItems.length; i++)
masterListMapping[masterListItems[i].Id] = true;
for (var subItem = 0; subItem < subItems.length; subItem++) {
if (masterListMapping[subItems[subItem].Id] == true) { //if it is a sub item
//do some UI specific thing
}
}
//example item is an object, ID is string
var item = { ID: "exampleID112233",
data: 4545 }; //sample item
var masterList = {}; //masterList as a dictionary
//for each item created, use its ID as its key.
masterList["exampleID112233"] = item;
var subCat1 = []; //sublist is an array of ID;
subCat1.push("exampleID112233");
//you can also make new sublists as array, push the item's ID in them.
var subCat2 = ["anotherID334455"];
//iterate through sublist
for (var i = 0; i < subCat1.length; i++) {
//access the referenced item
masterList[subCat1[i]].data += 4;
}
//DELETING: remove the ID from all sublists, then delete it from masterlist.
Why do you want to hardcode referencing when you have language constructs for that?
If you have unique id for items why don't you make them a hash effectively?
// effective {hash}
var masterListItems = {
uid_1: { /* item definition */ },
uid_2: { /* item definition */ },
uid_3: { /* item definition */ },
// ...
};
Then the subset of items can be represented in 3 ways:
// another hash
var subItems = {
uid_6: masterListItems["uid_6"], // effective referencing of the
uid_321: masterListItems["uid_321"], // masterList items
// ...
};
// or array of items
var subItems = [
masterListItems["uid_6"],
masterListItems["uid_321"],
// ...
];
// or array of ids
var subItems = [
"uid_6]",
"uid_321",
// ...
];
The tradeoffs:
hashes are good for uniquely indexed and
effective for lots of get/set operations
arrays are good for numerically indexed data, or when the most common usage is iteration