Removing outer array object if an inner array meets a condition - javascript

I am dealing with a fairly complex object. It contains 2 arrays, which contain 3 arrays each of objects:
I'm trying to delete one of the history: Array[2] if one of the objects in it has username: null.
var resultsArray = result.history;
var arrayCounter = 0;
resultsArray.forEach(function(item) {
item.forEach(function(innerItem) {
if (innerItem.username == null) {
resultsArray.splice(arrayCounter,1);
};
});
arrayCounter++;
});
Looking through answers it's recommended to do something like:
resultsArray.splice(arrayCounter,1);
This isn't working in this situation because more than one of the objects could have username == null and in that case it will delete multiple history objects, not just the one that I want.
How do I remove only the one specific history array index if username == null?

splice is evil. I think using immutable array methods like filter might be easier to reason about:
x.history =
x.history.filter(function (h) {
return !h.some(function (item) {
return item.username === null
})
})
Go through all the histories, and do not include them in the filter if they have a username that is null.

My understanding was that you only want to delete the first outer array that has an inner array that has an object with a null username. Heres one solution closest to your current form:
var resultsArray = result.history;
var arrayCounter = 0;
var foundFirstMatch = false;
resultsArray.forEach(function(item) {
if (!foundFirstMatch) {
item.forEach(function(innerItem) {
if (innerItem.username == null && !foundFirstMatch) {
foundFirstMatch = true;
};
});
arrayCounter++;
}
});
if (foundFirstMatch > 0)
resultsArray.splice(arrayCounter, 1);
Other syntax:
var resultsArray = result.history;
var outerNdx;
var innerNdx;
var foundMatch = false;
for (outerNdx = 0; !foundMatch && outerNdx < resultsArray.length; outerNdx++) {
for (innerNdx = 0; !foundMatch && innerNdx < resultsArray[outerNdx].length; innerNdx++) {
if (resultsArray[outerNdx][innerNdx].username == null) {
foundMatch = true;
}
}
}
if (foundMatch)
resultsArray.splice(outerNdx, 1);

Update - here's how I'd do it now, without lodash:
thing.history.forEach((arr, i) => {
thing.history[i] = arr.filter( (x) => x.username !== null );
});
Previous answer:
I'd use lodash like this:
_.each(thing.history, function(array, k){
thing.history[k] = _.filter(array, function(v){
return v.username !== null;
})
});
Here's a jsfiddle:
https://jsfiddle.net/mckinleymedia/n4sjjkwn/2/

You should write something like this:
var resultsArray = result.history.filter(function(item){
return !item.some(function(inner){ return inner.username==null; });
});

The foreach loop cant break in this way but a regular for loop can. This is working:
result.history.forEach(function(item) {
loop2:
for (var i = 0; i < item.length; i++) {
var innerItem = item[i];
console.log(innerItem);
break loop2;
}
});

Related

angularjs: check for an id exists in an array r not

var vm = this;
vm.usableDeposits = [];
for (var i = 0; i < billableChargeCodes.length; i++) {
var deposits = usableDeposits.filter(function(ud) {
return ud.billinggroupuid == billableChargeCodes[i].billinggroupuid ||
ud.billinggroupuid == billableChargeCodes[i].billingsubgroupuid ||
ud.departmentuid == billableChargeCodes[i].departmentuid ||
!ud.entypeuid ||
ud.entypeuid == entypeuid
})
for (var i = 0; i < deposits.length; i++) {
var depositid = deposits[i]._id;
first time, vm.usableDeposits[] is empty. I have to check deposits[i]._id exists in vm.usableDeposits[] or not. How to check vm.usableDeposits[] empty array _id with deposits[i]._id? if id not exists in vm.usableDeposits[], then i want to push the element into vm.usableDeposits[]
You can simply use the indexOf operator in JavaScript to check if a value exists in the array or not. It returns -1 if the value is not in the array, else, it returns the index of the element in the array.
The syntax goes like this -
if(myArray.indexOf(value) !== -1) {
// do this
} else {
// do that
}
Hope this helps.
You can use .some
deposits.some(o=> vm.usableDeposits.indexOf(o.id))
to check if the ID in deposits array is in vm.usableDeposits. .some will return true if condition is true and false otherwise
$scope.findIndexInData = function(data, property, value) {
var result = -1;
if((!!data) && (!!property) && (!!value)){
data.some(function (item, i) {
if (item[property] === value) {
result = i;
return true;
}
});
}
return result;
}
Pass on the array as the First Element. Property as second element and value you are looking in that array.
Example:
array = [{id:"1234",name:"abc"}, {id:"4567",name"xyz"}]
So you need to call:
index = $scope.findIndexInData(array , 'id' , 4567)

Get full value from array using partial value

I have an array like this:
var array = ["xs-1", "sm-10", "md-4"];
Now I want to get the number at the end of a particular value. For example I want to search the array for "md-" and see what number is at the end of that string (should return 4).
I can't do array.indexOf("xs-") because that isn't the whole value. Is there a way to do this?
Using a for loop:
var array = ["xs-1", "sm-10", "md-4"];
var search = "md-";
var found = null;
for (var i = 0; i < array.length; i++) {
if (array[i].indexOf(search) === 0) {
found = array[i];
break; // Note: this is assuming only one match exists - or at least you are
// only interested in the first match
}
}
if (found) {
alert(found);
} else {
alert("Not found");
}
Using .filter:
var array = ["xs-1", "sm-10", "md-4"];
var search = "md-";
var filtered = array.filter(function(item) {
return item.indexOf(search) === 0;
});
// note that here filtered will contain all matched elements, so it might be more than
// one match.
alert(filtered);
Building from #János Weisz's suggestion, you can easily transform your array into an object using .reduce:
var array = ["xs-1", "sm-10", "md-4"];
var search = "md";
var obj = array.reduce(function(prev, item) {
var cells = item.split("-");
prev[cells[0]] = cells[1];
return prev;
}, {});
// note: at this point we have an object that looks like this:
// { xs:1, sm:10, md: 4 }
// if we save this object, we can do lookups much faster than looping
// through an array
// now to find "md", we simply do:
alert(obj[search]);
If you need to do multiple look ups from the same source array, then transforming it into an object may be the most efficient approach overall. You pay the initial price of the transformation, but after than lookups are O(1) versus O(n) for each time you have to search your array. Of course, if you only ever need one item, then probably don't bother.
I recommend using objects for this:
var array = [{'type': 'xs', 'value': 1}, {'type' : 'sm', 'value': '10'}, {'type' : 'md', 'value': '4'}];
This way you can search the array as:
function searchMyArrayByType(array, type) {
var items[];
for (var i = 0; i < array.length; i++)
{
if (array[i].type == type) items.push(array[i].value);
}
return items;
}
var valuesWithMd = searchMyArrayByType(array, 'md');
For more information regarding the structure and use of objects, please refer to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects
You can create a method that takes the prefix you're looking for, the array, and the split character and returns all the numbers in an array:
function findNumberFromPrefix(prefix, arr, splitChar) {
var values = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i].indexOf(prefix) === 0) {
values.push(arr[i].split(splitChar)[1]);
}
}
return values;
}
And call it:
var array = ["xs-1", "sm-10", "md-4"];
var values = findNumberFromPrefix("md-", array, "-");
console.log(values); //["4"]
Demo: http://jsfiddle.net/rn4h9msh/
A more functional approach and assuming you can have have more than one element with the same prefix:
function findPrefix(array, prefix) {
return array.filter(function (a) { return a.indexOf(prefix) === 0; })
.map(function (e) { return e.slice(prefix.length); })
}
If you have only one matching element, do a loop like this:
var array = ["xs-1", "sm-10", "md-4"];
var needle = "md-";
for(i=0;i<array.length;i++) {
if(array[i].indexOf(needle) == 0)
alert(array[i].substr(needle.length, array[i].length));
}
Demo: http://jsfiddle.net/kg0c43ov/
You can do it like this...
var array = ["xs-1", "sm-10", "md-4"];
getValue("md-");
function getValue(search) {
for(var key in array) {
if(array[key].indexOf(search) > -1) {
alert("Array key is: " + key);
alert("Array value is: " + array[key].replace(search, ""));
}
}
}
JSFiddle here.

Best way to group elements in an array with least complexity

I have a JSON array which looks like this:
var map_results = [{"Type":"Flat","Price":100.9},
{"Type":"Room","Price":23.5},
{"Type":"Flat","Price":67.5},
{"Type":"Flat","Price":100.9}
{"Type":"Plot","Price":89.8}]
This array contains about 100,000 records. I want the output to be grouped by "Type" and "Price". It should look like this:
var expected_output = [{"Type":"Flat", "Data":[{"Price":100.9, "Total":2},
{"Price":67.5, "Total":1}] },
{"Type":"Room","Data":[{"Price":23.5,"Total":1}]},
{"Type":"Plot","Data":[{"Price":89.8, "Total:1"}]}]
This has to be done in pure javascript and I cannot use libraries like undersore.js. I tried solving the problem but it had like 3 nested for loops which made the complexity as n^4. What could be a better solution for this problem??
The function I have looks like this:
var reduce = function (map_results) {
var results = [];
for (var i in map_results) {
var type_found = 0;
for(var result in results){
if (map_results[i]["Type"] == results[result]["Type"]){
type_found = 1;
var price_found = 0;
for(var data in results[result]["Data"]){
if(map_results[i]["Price"] == results[result]["Data"][data]["Price"]){
price_found = 1;
results[result]["Data"][data]["Total"] +=1;
}
}
if(price_found == 0){
results[result]["Data"].push({"Price":map_results[i]["Price"], "Total":1});
}
}
}
if(type_found == 0){
results.push({"Type":map_results[i]["Type"], "Data":[{"Price":map_results[i]["Price"],"Total":1}]});
}
}
return results;
};
I have a short function that handles the first part of the requested functionality: It maps the map_results to the desired format:
var map_results = [{"Type":"Flat","Price":100.9},
{"Type":"Room","Price":23.5},
{"Type":"Flat","Price":67.5},
{"Type":"Flat","Price":100.9},
{"Type":"Plot","Price":89.8}]
var expected_output = map_results.reduce(function(obj, current){
if(!obj[current.Type]){
obj[current.Type] = {'Type':current.Type, 'Data':[]};
}
obj[current.Type].Data.push({'Price':current.Price, 'Total':1});
return obj;
},{})
Then this piece of code is required to calculate the totals, I'm afraid:
for(var type in expected_output){
var d = {};
for(var item in expected_output[type].Data){
d[expected_output[type].Data[item].Price] = (d[expected_output[type].Data[item].Price] || 0) + 1;
}
expected_output[type].Data = [];
for(var i in d){
expected_output[type].Data.push({
'Price':i,
'Total':d[i]
})
}
}
Output:
{
"Flat":{
"Type":"Flat",
"Data":[{"Price":"100.9","Total":2},
{"Price":"67.5","Total":1}]
},
"Room":{
"Type":"Room",
"Data":[{"Price":"23.5","Total":1}]
},
"Plot":{
"Type":"Plot",
"Data":[{"Price":"89.8","Total":1}]
}
}
As the Types and the Prices are unique after grouping I think a structure like {"Flat": {"100.9":2,"67.5":1}, {"Room": {"23.5": 1}}} would be easier to handle. So could do the grouping the following way:
var output = {};
map_results.map(function(el, i) {
output[el["Type"]] = output[el["Type"]] || [];
output[el["Type"]][el["Price"] = (output[el["Type"]][el["Price"]+1) || 1;
});
If you can not handle this structure you could do another mapping to your structure.
As you are iterating the Array one time this should have a complexity of n.
Look here for a working fiddle.
EDIT: So remap everything to your structure. The order of the remapping is far less then the first mapping, because the grouping is already done.
var expected_output = [];
for(type in output) {
var prices = [];
for(price in output[type]) {
prices.push({"Price": price, "Total": output[type][price]);
}
expected_output.push({"Type": type, "Data": prices});
}
Below is yet another effort. Here's a FIDDLE
For performance testing, I also mocked up a JSPerf test with 163840 elements. On Chrome(OSX) original solution is 90% slower than this one.
Few notes:
Feel free to optimize for your case (e.g. take out the hasOwnProperty check on object cloning).
Also, if you need the latest Total as the first element use unshift instead of push to add the obj the beginning of the array.
function groupBy(arr, key, key2) {
var retArr = [];
arr.reduce(function(previousValue, currentValue, index, array){
if(currentValue.hasOwnProperty(key)) {
var kVal = currentValue[key];
if(!previousValue.hasOwnProperty(kVal)) {
previousValue[kVal] = {};
retArr.push(previousValue[kVal]);
previousValue[kVal][key] = kVal;
previousValue[kVal]["Data"] = [];
}
var prevNode = previousValue[kVal];
if(currentValue.hasOwnProperty(key2)) {
var obj = {};
for(var k in currentValue) {
if(currentValue.hasOwnProperty(k) && k!=key)
obj[k] = currentValue[k];
}
obj["Total"] = prevNode["Data"].length + 1;
prevNode["Data"].push(obj);
}
}
return previousValue;
}, {});
return retArr;
}
var map_results = [{"Type":"Flat","Price":100.9},
{"Type":"Room","Price":23.5},
{"Type":"Flat","Price":67.5},
{"Type":"Flat","Price":100.9},
{"Type":"Plot","Price":89.8}];
var expected_output = groupBy(map_results, "Type", "Price");
console.dir(expected_output);
Tried something like this:
var reduce_func = function (previous, current) {
if(previous.length == 0){
previous.push({Type: current.Type, Data:[{Price:current.Price,Total:1}]});
return previous;
}
var type_found = 0;
for (var one in previous) {
if (current.Type == previous[one].Type){
type_found = 1;
var price_found = 0;
for(var data in previous[one].Data){
if(current.Price == previous[one].Data[data].Price){
price_found = 1;
previous[one].Data[data].Total += 1;
}
}
if(price_found == 0){
previous[one].Data.push({Price:current.Price, Total:1});
}
}
}
if(type_found == 0){
previous.push({Type:current.Type, Data:[{Price : current.Price ,Total:1}]});
}
return previous;
}
map_results.reduce(reduce_func,[]);

Finding my object inside JSON by its ID

[
{"ID":"5","Name":"Jay"},
{"ID":"30","Name":"Sharon"},
{"ID":"32","Name":"Paul"}
]
So I have this kind of JSON.
I need to easily supply the value for a required key.
For example:
30 would yield => "Sharon"
5 would yield => "Jay"
etc. What is the right way to do this?
Iterate the array and check if the ID matches
function getById(id) {
var O = null;
for (var i=0; i<arr.length; i++) {
if ( arr[i].ID == id ) return O = arr[i];
}
return O;
}
getById('30'); // returns {"ID":"30","Name":"Sharon"}
FIDDLE
or in newer browsers:
function getById(arr, id) {
return arr.filter(function(o) { return o.ID == id });
}
FIDDLE
Try a linear search:
var searchId = "30";
for(var i = 0; i < json.length; i++)
{
if(json[i].ID == searchId)
{
// Found it.
//
break;
}
}
If the IDs will be unique, and if you're going to need to do this frequently, then you may want to convert your collection to key/value pairs where the ID is the key.
var byId = data.reduce(function(res, obj) {
res[obj.ID] = obj;
return res
}, {});
Now you can simply use the ID to look up the object.
var target = byId["30"];
You could probably just write something to loop through it.
var data = [ {"ID":"5","Name":"Jay"},{"ID":"30","Name":"Sharon"}, {"ID":"32","Name":"Paul"} ];
for(var i in data){
if(data[i]["ID"] == 30){
return data[i]["Name"];
}
}
undersocre.js can find a object in collection by one line code
Reference: http://underscorejs.org/#find
Code:
var people = [
{"ID":"5","Name":"Jay"},
{"ID":"30","Name":"Sharon"},
{"ID":"32","Name":"Paul"}
];
_.find(people, function(person) { return person.ID === '5'; });
FIDDLE

Push objects to array so long as object property is not NaN

I am aware that I could use .filter to achieve this, however I am not sure how to implement it.
I have objects as follows within an array
item {
title : g.attributes.title,
category : g.attributes.categoryid,
possible: g.attributes.possible
}
however, some items in the array have a possible property of NaN.
I need to make sure that only items whose possible property is not NaN, are pushed into the array.
Here is an excerpt of my complete code:
function load(id){
itemPath = lev1.lev2.lev3;
items = [];
for (var i = 0; i<itemPath.length; i++) {
if(itemPath[i].attributes.id==id) {
return itemPath[i].attributes.grades.models.map(function(g) {
items.push(
{
title : g.attributes.title,
category : g.attributes.categoryid,
possible: g.attributes.possible
});
});
}
}
}
function load(id){
itemPath = lev1.lev2.lev3;
items = [];
for (var i = 0; i<itemPath.length; i++) {
if(itemPath[i].attributes.id==id) {
return itemPath[i].attributes.grades.models.map(function(g) {
if(g.attributes.possible !== g.attributes.possible){
return;
}
items.push(
{
title : g.attributes.title,
category : g.attributes.categoryid,
possible: g.attributes.possible
});
});
}
}
}
NaN is the only property in javascript that does not equal itself. Just loop over the properties and check them for this, or use the built in NaN() function within the loop as suggested elsewhere.
Update
Since you're only worried about the possible property, just check that one as part of the if statement using === self, or isNaN()
Just change your test line from
if(itemPath[i].attributes.id==id)
to use isNaN function on the properties you want to check
var attr = itemPath[i].attributes;
if (attr.id==id && !isNaN(attr.title) && !isNaN(attr.categoryid) && !isNaN(attr.possible))
You can use the isNaN() and test it before adding it...
function load(id){
itemPath = lev1.lev2.lev3;
items = [];
for (var i = 0; i<itemPath.length; i++) {
if(itemPath[i].attributes.id==id) {
return itemPath[i].attributes.grades.models.map(function(g) {
if( isNaN(g.attributes.title) || isNaN(g.attributes.categoryid) || isNaN(g.attributes.possible) ){
items.push(
{
title : g.attributes.title,
category : g.attributes.categoryid,
possible: g.attributes.possible
});
}
});
}
}
}
You're code is a little confusing
function load(id){
itemPath = lev1.lev2.lev3;
items = [];
for (var i = 0; i<itemPath.length; i++) {
if(itemPath[i].attributes.id==id) {
return itemPath[i].attributes.grades.models.map(function(g) {
items.push(
{
title : g.attributes.title,
category : g.attributes.categoryid,
possible: g.attributes.possible
});
});
}
}
}
It doesn't look like you are using map right. Map works like list compresions in the sense that it iterates over a sequence to preform some kind of operation on each element and returns a new sequence
var arr = [1,2,3];
var complexMagic = arr.map( function(n) { return n + 10; } );
// complexMagic === [11,12,13]
FYI, this is how filter, works. Filter takes in a predicate function( aka, Boolean function) to build a new sequence. If the predicate returns true, then the element will be stored in the new sequence.
var arr = [1, 123, 42, 1001, 1100];
var oddNumbers = arr.filter( function(n) {
return 1 === (n & (-n) );
} );
// oddNumbers === [1, 123, 1001] );
// Bit hacks are fun ;P
It looks like you don't need items array or to even push new elements onto it.
function load(id){
itemPath = lev1.lev2.lev3;
items = [];
for (var i = 0; i<itemPath.length; i++) {
if(itemPath[i].attributes.id==id) {
return itemPath[i].attributes.grades.models.map(function(g) {
// You don't have to return anything.
// This might be an ok place for the NaN check.
return ({
title : g.attributes.title,
category : g.attributes.categoryid,
possible: g.attributes.possible
});
});
}
}
}
I'm lazy and didn't testing any of my code so reader beware. Also avoid the push method if possible. It can be a inefficient approach to append new elements onto an array.

Categories

Resources