Related
PS: I have already searched the forums and have seen the relevant posts for this wherein the same post exists but I am not able to resolve my issue with those solutions.
I have 2 json objects
var json1 = [{uid:"111", addrs:"abc", tab:"tab1"},{uid:"222", addrs:"def", tab:"tab2"}];
var json2 = [{id:"tab1"},{id:"new"}];
I want to compare both these and check if the id element in json2 is present in json1 by comparing to its tab key. If not then set some boolean to false. ie by comparing id:"tab1" in json2 to tab:"tab1 in json1 .
I tried using below solutions as suggested by various posts:
var o1 = json1;
var o2 = json2;
var set= false;
for (var p in o1) {
if (o1.hasOwnProperty(p)) {
if (o1[p].tab!== o2[p].id) {
set= true;
}
}
}
for (var p in o2) {
if (o2.hasOwnProperty(p)) {
if (o1[p].tab!== o2[p].id) {
set= true;
}
}
}
Also tried with underscore as:
_.each(json1, function(one) {
_.each(json2, function(two) {
if (one.tab!== two.id) {
set= true;
}
});
});
Both of them fail for some test case or other.
Can anyone tell any other better method or outline the issues above.
Don't call them JSON because they are JavaScript arrays. Read What is JSON.
To solve the problem, you may loop over second array and then in the iteration check if none of the objects in the first array matched the criteria. If so, set the result to true.
const obj1 = [{uid:"111", addrs:"abc", tab:"tab1"},{uid:"222",addrs:"def", tab:"tab2"}];
const obj2 = [{id:"tab1"},{id:"new"}];
let result = false;
for (let {id} of obj2) {
if (!obj1.some(i => i.tab === id)) {
result = true;
break;
}
}
console.log(result);
Unfortunately, searching the forums and reading the relevant posts is not going to replace THINKING. Step away from your computer, and write down, on a piece of paper, exactly what the problem is and how you plan to solve it. For example:
Calculate for each object in an array whether some object in another array has a tab property whose value is the same as the first object's id property.
There are many ways to do this. The first way involves using array functions like map (corresponding to the "calculate for each" in the question, and some (corresponding to the "some" in the question). To make it easier, and try to avoid confusing ourselves, we'll do it step by step.
function calculateMatch(obj2) {
return obj2.map(doesSomeElementInObj1Match);
}
That's it. Your program is finished. You don't even need to test it, because it's obviously right.
But wait. How are you supposed to know about these array functions like map and some? By reading the documentation. No one help you with that. You have to do it yourself. You have to do it in advance as part of your learning process. You can't do it at the moment you need it, because you won't know what you don't know!
If it's easier for you to understand, and you're just getting started with functions, you may want to write this as
obj2.map(obj1Element => doesSomeElementInObj1Match(obj1Element))
or, if you're still not up to speed on arrow functions, then
obj2.map(function(obj1Element) { return doesSomeElementInObj1Match(obj1Element); })
The only thing left to do is to write doesSomeElementInObj2Match. For testing purposes, we can make one that always returns true:
function doesSomeElementInObj2Match() { return true; }
But eventually we will have to write it. Remember the part of our English description of the problem that's relevant here:
some object in another array has a tab property whose value is the same as the first object's id property.
When working with JS arrays, for "some" we have the some function. So, following the same top-down approach, we are going to write (assuming we know what the ID is):
In the same way as above, we can write this as
function doesSomeElementInObj2Match(id) {
obj2.some(obj2Element => tabFieldMatches(obj2Element, id))
}
or
obj2.some(function(obj2Element) { return tabFieldMatches(obj2Element, id); })
Here, tabFieldMatches is nothing more than checking to make sure obj2Element.tab and id are identical.
We're almost done! but we still have to write hasMatchingTabField. That's quite easy, it turns out:
function hasMatchingTabField(e2, id) { return e2.tab === id; }
In the following, to save space, we will write e1 for obj1Element and e2 for obj2Element, and stick with the arrow functions. This completes our first solution. We have
const tabFieldMatches = (tab, id) { return tab === id; }
const hasMatchingTabField = (obj, id) => obj.some(e => tabFieldMatches(e.tab, id);
const findMatches = obj => obj.some(e => hasMatchingTabField(e1, obj.id));
And we call this using findMatches(obj1).
Old-fashioned array
But perhaps all these maps and somes are a little too much for you at this point. What ever happened to good old-fashioned for-loops? Yes, we can write things this way, and some people might prefer that alternative.
top: for (e1 of obj1) {
for (e2 of (obj2) {
if (e1.id === e2.tab) {
console.log("found match");
break top;
}
}
console.log("didn't find match);
}
But some people are sure to complain about the non-standard use of break here. Or, we might want to end up with an array of boolean parallel to the input array. In that case, we have to be careful about remembering what matched, at what level.
const matched = [];
for (e1 of obj1) {
let match = false;
for (e2 of obj2) {
if (e1.id === e2.tab) match = true;
}
matched.push(match);
}
We can clean this up and optimize it bit, but that's the basic idea. Notice that we have to reset match each time through the loop over the first object.
I have an array that contains dates. and for some reason I can't get it to show on my screen I've been debugging for a few days now and I've tracked it down to a single line, but the line has worked before and I can't figure out what the issue might be.
The array looks like this:
var selectItems =
[ "05-26-2017", "06-02-2017", "06-09-2017",
"06-16-2017", "06-23-2017", "06-30-2017", "07-07-2017", "07-14-2017",
"07-21-2017", "07-28-2017"...];
It's passed as an argument from another function, but that's how it's showing in console.log().
I might be going about this the wrong way, maybe even a lot further around then I need to but this is what I've come up with:
1. function setTHead(selectItems) {
2 var formatString;
3. for (var x = 0; x < 12; x++) {
4. formatString = selectItems[x].replace(/[^0-9/-]/g, "").toString();
5. console.log(selectItems);
6. $('#datTab').append("<div id='col" + x + "' class='column'>'" + formatString + "'</div>");
7. }
8. }
the array up top is what's showing from the console.log 5 lines down.
the sixth line is what is seeming to give me issues. Nothing is put on the page at all.
I'm getting a console error saying:
jQuery.Deferred exception: selectItems is undefined setTHead#http://localhost/mySite/script.js:136:9
startUp2#http://localhost/mySite/script.js:146:5
#http://localhost/mySite/table.php:19:9
mightThrow#http://localhost/mySite/lib/jquery.js:3586:52
resolve/</process<#http://localhost/mySite/lib/jquery.js:3654:49
setTimeout handler*resolve/<#http://localhost/mySite/lib/jquery.js:3692:37
fire#http://localhost/mySite/lib/jquery.js:3320:30
fireWith#http://localhost/mySite/lib/jquery.js:3450:29
fire#http://localhost/mySite/lib/jquery.js:3458:21
fire#http://localhost/mySite/lib/jquery.js:3320:30
fireWith#http://localhost/mySite/lib/jquery.js:3450:29
ready#http://localhost/mySite/lib/jquery.js:3923:13
completed#http://localhost/mySite/lib/jquery.js:3933:9
EventListener.handleEvent*#http://localhost/mySite/lib/jquery.js:3949:9
#http://localhost/mySite/lib/jquery.js:39:9
#http://localhost/mySite/lib/jquery.js:17:3
undefined
followed by:
TypeError: selectItems is undefined
and thats pointing to line 6.
if anyone has any advice I would be very much appreciative. Thank you in advance.
EDIT: A little more code:
function startTblView(defSel) {
if (defSel === true) {
setCookie('defSel', true, 7);
} else{
setCookie('defSel', false, 7);
}
saveSelected();
window.open('table.php', '_self');
defSel = getCookie('defSel');
if (defSel) {
selectItems = getDefDates();
}else {
selectItems = reGetSelected();
}
setTHead(selectItems);
}
defSel, is a boolean passed from my last page stating whether I'm doing a default view or a custom view, the custom view is passed from saveSelected();
saveSelected is a function for just saving the selected global value as a cookie so I can pull it out on the next page.
getDefDates pulls the default values for the array
reGetSelected, gets the selected array from the cookie.
I apologize for wonky naming conventions. I'm the only one working on this site and I'm just making sure the names don't overlap.
You can do this :
HTML code
<div id="datTab"></div>
JS code
var selectItems =
[ "05-26-2017", "06-02-2017", "06-09-2017",
"06-16-2017", "06-23-2017", "06-30-2017", "07-07-2017", "07-14-2017",
"07-21-2017", "07-28-2017"];
function setTHead(selectItems) {
var formatString;
$.each( selectItems, function( index, value ){
formatString = value.replace(/[^0-9/-]/g, "").toString();
$('#datTab').append("<div id='col" + index + "' class='column'>'" + value + "'</div>");
});
};
You can use $.each, its better than 'for' with javascript.
The .each() method is designed to make DOM looping constructs concise
and less error-prone. When called it iterates over the DOM elements
that are part of the jQuery object. Each time the callback runs, it is
passed the current loop iteration, beginning from 0. More importantly,
the callback is fired in the context of the current DOM element, so
the keyword this refers to the element.
I did a JsFiddle
Here.
I have an array that looks something like this.
Users : {
0 : { BidderBadge: "somestuff", Bidders: 6, }
1 : { BidderBadge: "somemorestuff", Bidders: 7,}
}
I want to search the array using lodash to find a value inside of each of the user objects.
Specifically, I want to use values from another similar array of objects to find the value.
var bidArray = [];
_.each(this.vue.AllUsers, function(user) {
_.each(this.vue.Bids, function(bid) {
if(user.BidderBadge == bid.Badge) {
bidArray.push(user);
}
});
});
This is what I have and it works, but I want to do it using only one loop instead of two. I want to use something like _.indexOf. Is that possible?
If you want to avoid nesting, you just have to modify Azamantes' solution a bit
var bidders = this.vue.Bids.reduce(function(acc, bid) {
return acc[bid.BidderBadge] = true;
}, {});
var bidArray = this.vue.AllBidders.filter(function(bidder) {
return !!bidders[bidder.Badge];
});
It is difficult to give an accurate answer with an example that doesn't coincide with the input that your provide.
Anyway, supposing your data structures were more or less like this ones, you could solve the problem with lodash _.intersectionWith.
Intersect both arrays using a comparator that checks the correct object properties. Also, take into account that users must go first in the intersection due to the fact that you're interested in its values.
function comparator(user, bid) {
return user.BidderBadge === bid.Badge;
}
console.log(_.intersectionWith(users, bids, comparator));
Here's the fiddle.
I'm allowing users to highlight rows in a table by clicking them.
When they click them, HighToggle() is called.
HighToggle() checks to to see if the bgcolor of the td matches the highlighter color and either highlights or un-highlights.
function HighToggle(cObj,vid,vColor,hColor) {
if ((vColor == hColor)) {
$(cObj).css('background-color','#FFFFFF'); // un-highlight
vx.push({'id' : vid, 'status' : 'off'});
} else {
$(cObj).css('background-color','#' + Highlighter.Hex); // highlight
vx.push({'id' : vid, 'status' : 'on'});
}
}
It is important that I store these in an array, because I have recordset navigation that deletes the rows (from view) and loads in new rows via JSON. Otherwise, when I wanted to submit the data to the server, I could just loop through table rows and build the array pre-submit.
So, that works fine, except that it adds duplicate id's in the case of each time something is highlighted or un-highlighted, so it might look like:
1: on, 7: on, 7: off, 2: on, 4: on, 1: off, 3: on, 1: on, 2: off.
Which is just a mess.
Following the same click process that results in that array above, what I'd like to end up with is..
1: on, 7: off, 2: off, 4: on, 3: on
Rows never interacted with (5, 6, 8, 9, 10 if the result set only had 10 rows), need not have an entry in the array at all.
I'm looking for a fast way to search my array and override rows if they exist.
I have thought of one solution that I haven't tried to implement yet because I feel like there might be something better.
I could make a separate array, let's say it's called vindexes. Vindexes could use the ID passed as the vid variable like so..
vindexes[vid] = vx.length;
HighToggle could check to see if vindexes[vid] exists, and if so, use that id to override rather than vx.push.
It would work (wouldn't it?) but is it reinventing the wheel? Is there a faster way? This function will be used a lot on each page.
I've found the grep function, since I'm using jQuery, but I don't think grep is applicable to me here since I want to know the index of where the ID was found.
Thank you for any advice.
Edit: Here's what I devised myself, though I think that the answer is a more elegant and sensible solution.
function HighToggle(cObj,vid,vColor,hColor) {
vid = Number(vid);
if (!(vid in vi)) {
inIndex = vx.length;
} else {
inIndex = vi[vid];
}
if ((vColor == hColor)) {
$(cObj).css('background-color','#FFFFFF');
vi[vid] = inIndex;
vx[inIndex] = {'id' : vid, 'status' : 'off'};
} else {
vi[vid] = inIndex;
$(cObj).css('background-color','#' + Highlighter.Hex);
vx[inIndex] = {'id' : vid, 'status' : 'on'};
}
}
My solution, and the answer are both effective for my use, but perhaps someone else will run into this situation and benefit from the code I've provided.
You could use an object:
var vx = {};
function HighToggle(cObj,vid,vColor,hColor) {
if ((vColor == hColor)) {
$(cObj).css('background-color','#FFFFFF'); // un-highlight
vx[vid] = "off";
} else {
$(cObj).css('background-color','#' + Highlighter.Hex); // highlight
vx[vid] = "on";
}
}
Then if you need an array later (for submitting to server, etc):
var vxArray = [];
for (var key in vx) {
vxArray.push({id: key, status: vx[key]});
}
I'd like to come up with a good way to have a "suggested" order for how to sort an array in javascript.
So say my first array looks something like this:
['bob','david','steve','darrel','jim']
Now all I care about, is that the sorted results starts out in this order:
['jim','steve','david']
After that, I Want the remaining values to be presented in their original order.
So I would expect the result to be:
['jim','steve','david','bob','darrel']
I have an API that I am communicating with, and I want to present the results important to me in the list at the top. After that, I'd prefer they are just returned in their original order.
If this can be easily accomplished with a javascript framework like jQuery, I'd like to hear about that too. Thanks!
Edit for clarity:
I'd like to assume that the values provided in the array that I want to sort are not guaranteed.
So in the original example, if the provided was:
['bob','steve','darrel','jim']
And I wanted to sort it by:
['jim','steve','david']
Since 'david' isn't in the provided array, I'd like the result to exclude it.
Edit2 for more clarity:
A practical example of what I'm trying to accomplish:
The API will return something looking like:
['Load Average','Memory Usage','Disk Space']
I'd like to present the user with the most important results first, but each of these fields may not always be returned. So I'd like the most important (as determined by the user in some other code), to be displayed first if they are available.
Something like this should work:
var presetOrder = ['jim','steve','david']; // needn't be hardcoded
function sortSpecial(arr) {
var result = [],
i, j;
for (i = 0; i < presetOrder.length; i++)
while (-1 != (j = $.inArray(presetOrder[i], arr)))
result.push(arr.splice(j, 1)[0]);
return result.concat(arr);
}
var sorted = sortSpecial( ['bob','david','steve','darrel','jim'] );
I've allowed for the "special" values appearing more than once in the array being processed, and assumed that duplicates should be kept as long as they're shuffled up to the front in the order defined in presetOrder.
Note: I've used jQuery's $.inArray() rather than Array.indexOf() only because that latter isn't supported by IE until IE9 and you've tagged your question with "jQuery". You could of course use .indexOf() if you don't care about old IE, or if you use a shim.
var important_results = {
// object keys are the important results, values is their order
jim: 1,
steve: 2,
david: 3
};
// results is the orig array from the api
results.sort(function(a,b) {
// If compareFunction(a, b) is less than 0, sort a to a lower index than b.
// See https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/sort
var important_a = important_results[a],
important_b = important_results[b],
ret;
if (important_a && !important_b) {ret = -1}
else if (important_b && !important_a) {ret = 1}
else if (important_a && important_b) {ret = important_a - important_b}
else {ret = 0}; // keep original order if neither a or b is important
return(ret);
}
)
Use a sorting function that treats the previously known important results specially--sorts them to the head of the results if present in results.
items in important_results don't have to be in the results
Here's a simple test page:
<html>
<head>
<script language="javascript">
function test()
{
var items = ['bob', 'david', 'steve', 'darrel', 'jim'];
items.sort(function(a,b)
{
var map = {'jim':-3,'steve':-2,'david':-1};
return map[a] - map[b];
});
alert(items.join(','));
}
</script>
</head>
<body>
<button onclick="javascript:test()">Click Me</button>
</body>
</html>
It works in most browsers because javascript typically uses what is called a stable sort algorithm, the defining feature of which is that it preserves the original order of equivalent items. However, I know there have been exceptions. You guarantee stability by using the array index of each remaining item as it's a1/b1 value.
http://tinysort.sjeiti.com/
I think this might help. The $('#yrDiv').tsort({place:'start'}); will add your important list in the start.
You can also sort using this function the way you like.
Live demo ( jsfiddle seems to be down)
http://jsbin.com/eteniz/edit#javascript,html
var priorities=['jim','steve','david'];
var liveData=['bob','david','steve','darrel','jim'];
var output=[],temp=[];
for ( i=0; i<liveData.length; i++){
if( $.inArray( liveData[i], priorities) ==-1){
output.push( liveData[i]);
}else{
temp.push( liveData[i]);
}
}
var temp2=$.grep( priorities, function(name,i){
return $.inArray( name, temp) >-1;
});
output=$.merge( temp2, output);
there can be another way of sorting on order base, also values can be objects to work with
const inputs = ["bob", "david", "steve", "darrel", "jim"].map((val) => ({
val,
}));
const order = ["jim", "steve", "david"];
const vMap = new Map(inputs.map((v) => [v.val, v]));
const sorted = [];
order.forEach((o) => {
if (vMap.has(o)) {
sorted.push(vMap.get(o));
vMap.delete(o);
}
});
const result = sorted.concat(Array.from(vMap.values()));
const plainResult = result.map(({ val }) => val);
Have you considered using Underscore.js? It contains several utilities for manipulating lists like this.
In your case, you could:
Filter the results you want using filter() and store them in a collection.
var priorities = _.filter(['bob','david','steve','darrel','jim'],
function(pName){
if (pName == 'jim' || pName == 'steve' || pName == 'david') return true;
});
Get a copy of the other results using without()
var leftovers = _.without(['bob','david','steve','darrel','jim'], 'jim', 'steve', 'david');
Union the arrays from the previous steps using union()
var finalList = _.union(priorities, leftovers);