I am creating a search bar, that filters an array with French city names that match the input value with each keyup. I am getting some results back (using the console log), but the results are not what I expect and the IF condition I am using doesn't seem to be functioning...
Based in JavaScript and a little jQuery to get my list of cities from a text file, I've tried using indexOf and match function, but using slice() seems to have got me the furthest. I'm missing something somewhere to push the matched results into a new variable/array.
var cityArray = [];
$.get("liste.txt", function (data) {
cityArray = data.split("\n").sort();
console.log(cityArray);
});
$("#myInput").keyup(function () {
var searchedWord = $(this).val();
var counter = searchedWord.length;
var result = [];
console.log(searchedWord);
// console.log(typeof searchedWord);
// console.log(counter);
var matchedCities = 0;
if (counter < 3) {
return;
}
for (var i = 0; i < 100; i++) {
var city = cityArray.slice(0, cityArray.length);
console.log(city[i].slice(0, searchedWord.length));
if (city[i].slice(0, searchedWord.length) == searchedWord) {
result.push(city[i]);
console.log(result);
matchedCities++;
console.log(matchedCities);
if (matchedCities > 5) {
break;
}
}
}
});
So I am expecting to see matched cities with the input, in the console log. BUT, the function seems to break down before then as the result and matchedCities variables don't seem to change even when I know I have typed a city that is in the list.
You can use Array.filter and Array.includes to do your search. Something like this:
$("#myInput").keyup(function () {
var searchedWord = $(this).val();
var result = cityArray.filter(c => c.toLowerCase().includes(searchedWord.toLowerCase()))
console.log(result)
});
You can add the rest of the code you need but the idea is to "search" in this fashion.
I'm attempting to use JS to check a string for the most amount of the letters and append that result to a div. It looks so far like this:
var string = "AABBCCDDEEEEEEEEEE";
var stringInput = document.getElementById("string");
function showMostChar() {
var newInput = string.split("");
var inputAsArray = Array.of(newInput);
for (i = 0; i > inputAsArray; i++) {
if ((inputAsArray.indexOf("E")) > 9 ) {
stringInput.innerHTML = "E";
}
}
}
window.onload = showMostChar;
<div id="string"></div>
In plain english, I'm looking to split a string by character, which I then use to create an array. I loop through the array and if E has more than 9 (an arbitrary value) put E into the div. before I was using array() but I remembered thats not a valid function, so I did research and found Array.of() but I'm not seeing anything appended to string div.
is Array.of() not the right way to create an array in this context? If not why?
The ideal scenario is to log the total amount of characters that appear the most, so in this instance EEEEEEEEEE. I need to somehow get the value of each character and compare it to each other, somehow...something like:
for (i = 0; i > inputAsArray; i++) {
// somehow compare values to each other? e being each instance
// so, would that be a loop within a loop?
if ((i.value()) > e ) {
stringInput.innerHTML = i;
}
}
There are few things that needs to be fixed in your code:
First of all .split() will return an array so newInput is
already an array, you don't need any transform it.
In the other hand the right method to create a new array is
Array.from(), because Array.of() takes a Number as
argument.
Second thing you don't need any loop to check for the .indexOf(), you can directly call it upon newInput array.
And it wasn't entering the if block because .indexOf() will return
the first index of E in the array, which is lower than 9, I
used 5 for testing purpose here.
This is how should be your code:
function showMostChar() {
var newInput = string.split("");
if ((newInput.indexOf("E")) > 5 ) {
stringInput.innerHTML = "E";
}
}
Demo:
var string = "AABBCCDDEEEEEEEEEE";
var stringInput = document.getElementById("string");
function showMostChar() {
var newInput = string.split("");
console.log(newInput);
if ((newInput.indexOf("E")) > 5) {
stringInput.innerHTML = "E";
}
}
window.onload = showMostChar;
<div id="string"></div>
Edit:
To get the number of occurrences of E in the array, you can use Array.filter() like this:
function getNumberOfOccurences(char) {
var newInput = string.split("");
return newInput.filter(function(c){
return c === char;
}).length;
}
console.log(getNumberOfOccurences("E"));
Demo:
var string = "AABBCCDDEEEEEEEEEE";
var stringInput = document.getElementById("string");
function getNumberOfOccurences(char) {
var newInput = string.split("");
return newInput.filter(function(c){
return c === char;
}).length;
}
console.log(getNumberOfOccurences("E"));
Please, can you check my code where is the error? It should loop trough 1 array to choose each string and then loop through second array and check, if the value from second string contains value of first string.
for (var i = 0; i < oldLines.length; i++){
var subStringEach = oldLines[i];
var subStringEachNoDash = subStringEach.replace(/[^a-z0-9]/g,'');
// read New URLs and line by line save them as an object
var newLines = $('#newUrl').val().split(/\n/);
var newUrlResult = [];
for (var j = 0; j < newLines.length; j++){
var newUrlString = newLines[j];
var newUrlStringNoDash = newUrlString.replace(/[^a-z0-9]/g,'');
var isThere = newUrlStringNoDash.search(subStringEachNoDash);
if (isThere !== -1 ) {
newUrlResult[i] = newLines[j];
}
else {
newUrlResult[i] = "";
}
}
stockData.push({OldURL:oldLines[i],SearchSubstring:subStringEach,NewURL:newUrlResult[i]});
}
Now it finds only part of the results.. I place to first array:
anica-apartment
casa-calamari-real
ostrovni-apartman
and to the second array:
http://tempweb3.datastack.cz/be-property/anica-apartment/
http://tempweb3.datastack.cz/be-property/ostrovni-apartman/
http://tempweb3.datastack.cz/be-property/st-michael-apartment/
http://tempweb3.datastack.cz/be-property/casa-calamari-real/
and it will only find and return casa-calamari-real, http://tempweb3.datastack.cz/be-property/casa-calamari-real/ and the others returns empty..
Any ideas please?
Here is the full code on Codepen: https://codepen.io/vlastapolach/pen/VWRRXX
Once you find a match you should exit the inner loop, otherwise the next iteration of that loop will clear again what you had matched.
Secondly, you should use push instead of accessing an index, as you don't know how many results you will have. And as a consequence you will need to relate the find string with it (because i will not be necessary the same in both arrays)
So replace:
if (isThere !== -1 ) {
newUrlResult[i] = newLines[j];
}
else {
newUrlResult[i] = "";
}
with this:
if (isThere !== -1 ) {
newUrlResult.push({
searchSubstring: subStringEach,
newURL: newUrlString
});
break; // exit loop
}
At the end, just output newUrlResult.
NB: If you want to leave the possibility that a search string matches with more than one URL, then you don't need the break. The push will then still prevent you from overwriting a previous result.
I see that you solved already) But maybe you will like this code too)
newUrlResult variable could be a string I guess, because loop breaks when one value is found. If no values where found there will be just empty string. And I'm not sure you need to call newLines = $('#newUrl').val().split(/\n/) on every iteration.
var stockData = [];
oldLines.map(function(oldLine){
var cleanOldLine = oldLine.replace(/[^a-z0-9]/g,''),
newLines = $('#newUrl').val().split(/\n/),
newUrlResult = '';
for (var j = 0; j < newLines.length; j++){
var newLine = newLines[j],
cleanNewLine = newLine.replace(/[^a-z0-9]/g,''),
ifExists = cleanNewLine.search(cleanOldLine);
if (ifExists !== -1) {
newUrlResult = newLine;
break;
}
}
stockData.push({OldURL:oldLine, SearchSubstring:cleanOldLine, NewURL:newUrlResult});
});
I am doing the below to get certain nodes from a treeview followed by getting text from those nodes, filtering text to remove unique and then appending custom image to the duplicate nodes.
For this I am having to loop 4 times. Is there is a simpler way of doing this? I am worried about it's performance for large amount of data.
//Append duplicate item nodes with custom icon
function addRemoveForDuplicateItems() {
var treeView = $('#MyTree').data('t-TreeView li.t-item');
var myNodes = $("span.my-node", treeView);
var myNames = [];
$(myNodes).each(function () {
myNames.push($(this).text());
});
var duplicateItems = getDuplicateItems(myNames);
$(myNodes).each(function () {
if (duplicateItems.indexOf($(this).text()) > -1) {
$(this).parent().append(("<span class='remove'></span>"));
}
});
}
//Get all duplicate items removing unique ones
//Input [1,2,3,3,2,2,4,5,6,7,7,7,7] output [2,3,3,2,2,7,7,7,7]
function getDuplicateItems(myNames) {
var duplicateItems = [], itemOccurance = {};
for (var i = 0; i < myNames.length; i++) {
var dept = myNames[i];
itemOccurance[dept] = itemOccurance[dept] >= 1 ? itemOccurance[dept] + 1 : 1;
}
for (var item in itemOccurance) {
if (itemOccurance[item] > 1)
duplicateItems.push(item);
}
return duplicateItems;
}
If I understand correctly, the whole point here is simply to mark duplicates, right? You ought to be able to do this in two simpler passes:
var seen = {};
var SEEN_ONCE = 1;
var SEEN_DUPE = 2;
// First pass, build object
myNodes.each(function () {
var name = $(this).text();
var seen = seen[name];
seen[name] = seen ? SEEN_DUPE : SEEN_ONCE;
});
// Second pass, append node
myNodes.each(function () {
var name = $(this).text();
if (seen[name] === SEEN_DUPE) {
$(this).parent().append("<span class='remove'></span>");
}
});
If you're actually concerned about performance, note that iterating over DOM elements is much more of a performance concern than iterating over an in-memory array. The $(myNodes).each(...) calls are likely significantly more expensive than iteration over a comparable array of the same length. You can gain some efficiencies from this, by running the second pass over an array and only accessing DOM nodes as necessary:
var names = [];
var seen = {};
var SEEN_ONCE = 1;
var SEEN_DUPE = 2;
// First pass, build object
myNodes.each(function () {
var name = $(this).text();
var seen = seen[name];
names.push(name);
seen[name] = seen ? SEEN_DUPE : SEEN_ONCE;
});
// Second pass, append node only for dupes
names.forEach(function(name, index) {
if (seen[name] === SEEN_DUPE) {
myNodes.eq(index).parent()
.append("<span class='remove'></span>");
}
});
The approach of this code is to go through the list, using the property name to indicate whether the value is in the array. After execution, itemOccurance will have a list of all the names, no duplicates.
var i, dept, itemOccurance = {};
for (i = 0; i < myNames.length; i++) {
dept = myNames[i];
if (typeof itemOccurance[dept] == undefined) {
itemOccurance[dept] = true;
}
}
If you must keep getDuplicateItems() as a separate, generic function, then the first loop (from myNodes to myNames) and last loop (iterate myNodes again to add the span) would be unavoidable. But I am curious. According to your code, duplicateItems can just be a set! This would help simplify the 2 loops inside getDuplicateItems(). #user2182349's answer just needs one modification: add a return, e.g. return Object.keys(itemOccurance).
If you're only concerned with ascertaining duplication and not particularly concerned about the exact number of occurrences then you could consider refactoring your getDuplicateItems() function like so:
function getDuplicateItems(myNames) {
var duplicateItems = [], clonedArray = myNames.concat(), i, dept;
for(i=0;i<clonedArray.length;i+=1){
dept = clonedArray[i];
if(clonedArray.indexOf(dept) !== clonedArray.lastIndexOf(dept)){
if(duplicateItems.indexOf(dept) === -1){
duplicateItems.push(dept);
}
/* Remove duplicate found by lastIndexOf, since we've already established that it's a duplicate */
clonedArray.splice(clonedArray.lastIndexOf(dept), 1);
}
}
return duplicateItems;
}
I have this 2D array as follows:
var data = [[1349245800000, 11407.273], [1349247600000, 12651.324],
[1349249400000, 11995.017], [1349251200000, 11567.533],
[1349253000000, 11126.858], [1349254800000, 9856.455],
[1349256600000, 8901.779], [1349258400000, 8270.123],
[1349260200000, 8081.841], [1349262000000, 7976.148],
[1349263800000, 7279.652], [1349265600000, 6983.956],
[1349267400000, 7823.309], [1349269200000, 6256.398],
[1349271000000, 5487.86], [1349272800000, 5094.47],
[1349274600000, 4872.403], [1349276400000, 4168.556],
[1349278200000, 4501.939], [1349280000000, 4150.769],
[1349281800000, 4061.599], [1349283600000, 3773.741],
[1349285400000, 3876.534], [1349287200000, 3221.753],
[1349289000000, 3330.14], [1349290800000, 3147.335],
[1349292600000, 2767.582], [1349294400000, 2638.549],
[1349296200000, 2477.312], [1349298000000, 2270.975],
[1349299800000, 2207.568], [1349301600000, 1972.667],
[1349303400000, 1788.853], [1349305200000, 1723.891],
[1349307000000, 1629.002], [1349308800000, 1660.084],
[1349310600000, 1710.227], [1349312400000, 1708.039],
[1349314200000, 1683.354], [1349316000000, 2236.317],
[1349317800000, 2228.405], [1349319600000, 2756.069],
[1349321400000, 4289.437], [1349323200000, 4548.436],
[1349325000000, 5225.245], [1349326800000, 6261.156],
[1349328600000, 8103.636], [1349330400000, 10713.788]]
How do I get the index of value 1349247600000 in the array? I have tried $.inArray(1349247600000, data) but as expected this fails. Is there any other way or do I have to iterate over each? I am reluctant to add another loop to my process
This is a typical performance versus memory issue. The only way (that I know of) to avoid looping through the array, would be to maintain a second data structure mapping the timestamps to the index of the array (or whatever data might needed).
So you would have
var data = [
[1349245800000, 11407.273],
[1349247600000, 12651.324],
// ...
[1349330400000, 10713.788]
];
// the timestamps pointing at their respective indices
var map = {
'1349245800000': 0, // 0
'1349247600000': 1, // 1
// ...
'1349330400000': 42, // n - 1 (the length of the data array minus one)
}
This way, you use more memory, but have a constant lookup time when needing the index of the item in the array that a given timestamp belongs to.
To get the index of a given timestamp do:
map['1349247600000']; // resulting in 1 (e.g.)
If the data structure is dynamically changed, you would of course need to maintain the map data structure, but depending on the context in which you need the lookup, the constant time lookup can potentially be a real time saver compared to a linear time lookup.
I think you need a different data structure.
Try using a standard javascript object ({ key: value } - sometimes called a map or dictionary) to express your data. Looking up keys in an object is highly optimized (using something called hash tables).
If the index in your array has any meaning, store it as a property (typically named _id).
Ideally you should be using an object for this:
var data = {
'1349247600000': 12651.324
}
which you can access like:
data['1349247600000'];
However, this might be a nice solution (IE9 and above) in the meantime:
var search = 1349247600000;
function findIndex(data, search) {
var filter = data.filter(function (el, i) {
el.unshift(i);
return el[1] === search;
});
return filter[0][0];
}
console.log(findIndex(data, search));
fiddle : http://jsfiddle.net/CLa56/
var searchElement = 1349251200000;
var strdata = data.toString();
var newdata = eval("[" + strdata + "]");
var indexsearch = newdata.indexOf(searchElement);
var index = indexsearch/2; // 2 because array.length = 2
var params = {id: 1349251200000, index: -1};
data.some(function (e, i) {
if (e[0] === this.id) {
this.index = i;
return true;
}
}, params);
console.log(params.index);
jsfiddle
MDN|some Array Method
Note that this solution stops iterating after found, not necessarily over the entire array, so could be much faster for large arrays.
What about a custom cross browser solution ?
function findIndexBy(a, fn) {
var i = 0, l = a.length;
for (; i < l; i++) {
if (fn(a[i], i)) {
return i;
}
}
return -1;
}
Usage :
var list = [[1],[2],[3]], idx;
// idx === 1
idx = findIndexBy(list, function (item, i) {
return item[0] === 2;
});
// idx === -1
idx = findIndexBy(list, function (item, i) {
return item[0] === 4;
});