I have array of objects of my class. My class have variable for name that is unique for every object created. Now, I want to find, say, object in array that has a name "test". I wanted to try something like creating second array with just names as elements and when I create new object in my first array to create object in second array so that they share index number. Something like this:
arrayOfObjects.push(new obj("test"));
arrayOfNames.push("test");
function findIndexNumberOf(name){
for(var i = 0; i < arrayOfNames.length; i++){
if(arrayOfNames[i] === name)
return i;
}
}
But I think that this is pretty robust solution so I'm wondering is there better/smarter/faster way of doing this.
If you want to find the index of an item, it's generally the easiest to use indexOf:
const haystack = ["this", "is", "a", "test"];
const needle = "this";
const result = haystack.indexOf(needle);
However, this will work with primitive types. Assuming you have an array of objects, comparing them will require a different approach. Here are some one-liners:
const haystack = [new String("this"), new String("is"), new String("a"), new String("test")];
const needle = "test";
const result1 = haystack.indexOf(haystack.filter(item => item == needle)[0]);
const result2 = haystack.indexOf(haystack.filter(item => item.toString() === needle.toString())[0]);
const result3 = haystack.map(item => item.toString()).indexOf(needle.toString());
const result4 = haystack.indexOf(haystack.find(item => item.toString() === needle.toString()));
result1 filters the haystack using the == operator, thus ignoring the fact that the compared values are actually different types. The first element of the filtered array is then fed to indexOf. This will allow you to use a primitive string as the needle and search through a haystack of objects.
result2 uses the same approach, but casts both compared values to a primitive string, making sure they're both of the same type. This will allow you to mix and match primitives and String objects liberally, both in the haystack and needle.
result3 maps all haystack values to primitive strings and then uses indexOf on that new array of primitives. I also added toString to the needle to ensure it's also a primitive. This works similar to your approach, but the mapping is run every time you search for a needle. This is probably suboptimal.
result4 uses Array.prototype.find to locate the target object on the haystack and then feeds the result to indexOf. This might be the fastest, but I have no empirical data to back this up.
Now, in case you wanted to find the actual item, not just its index, you're best off using Array.prototype.find:
const result = haystack.find(item => item == needle);
or, in case both are String objects:
const result = haystack.find(item => item.toString() === needle.toString());
You can use the find method of Arrays object:
const myObject = arr.find(o => o.name === 'the_name_you_search');
Related
I'm trying to filter a set of objects based on values in one of their elements based on another array. I've got it working with filter just fine if the search is "OR" - it returns give me all objects for which at least one of the strings in the search array is found.
But I can't figure out how to make it work as an AND search - returning only the objects that match ALL of the strings in the search array.
Example:
struct Schedule {
let title: String
let classTypes: [String]
}
let schedule1 = Schedule(title: "One", classTypes: ["math","english","chemistry","drama"])
let schedule2 = Schedule(title: "Two", classTypes: ["pe","math","biology"])
let schedule3 = Schedule(title: "Three", classTypes: ["english","history","math","art"])
let schedules = [schedule1, schedule2, schedule3]
let searchArray = ["math", "english"]
//works for OR - "math" or "english"
var filteredSchedules = schedules.filter { $0.classTypes.contains(where: { searchArray.contains($0) }) }
I'd like to find a way for it to use the same search array
let searchArray = ["math", "english"]
But only return items 1 & 3 - as they both have BOTH math and english in the list.
There are good examples of AND conditions when the AND is across different search criteria: car type and colour - but I've been unable to find an example where the criteria are dynamically based on items in an array. For context, I could have dozens of schedules with 20+ class types.
You can work with a Set, isSubset will return true if the schedules element contains all elements of the searchSet
let searchSet = Set(searchArray)
var filteredSchedules = schedules.filter { searchSet.isSubset(of: $0.classTypes) }
As suggested by #LeoDabus it might be worth changing the type of classTypes to Set instead of arrays (if order doesn't matter) since they seems to be unique and then the filtering can be done in the opposite way without the need to convert searchArray each time
var filteredSchedules = schedules.filter { $0.classTypes.isSuperset(of: searchArray) }
The problem with the ECMA standard for sort of Object.keys() is known:
Object.keys() handle all keys with integer (example: 168), including integer as strings (example: "168"), as a integer. The result is, both are the same (168 === "168"), and overwrite itself.
var object = {};
object["168"] = 'x';
object[168] = 'y';
Object.keys(object); // Array [ "168" ]
object[Object.keys(object)]; // "y"
Interestingly, all keys (including pure integer keys) are returned as a string.
The ecma262 wrote about this: All keys will be handle as a integer, expect the key is a String but is not an array index.
https://tc39.es/ecma262/#sec-ordinaryownpropertykeys
That should tell us: 168 === "168". A toString() do not solve the problem.
var object = {};
object[[3].toString()] = 'z';
object[[1].toString()] = 'x';
object[[2].toString()] = 'y';
Object.keys(object);
// Array(3) [ "1", "2", "3" ]
Paradoxically, in this case, only integer apply as "enumerable" (it's ignoring array.sort(), that sort also strings with letters.).
My question about this is simple: How can i prevent the sort function in Object.keys()? I have testet the Object.defineProperties(object, 1, {value: "a", enumerable: true/false}), but that mean not realy enumerable in the case of integer or string or integer-like string. It means only should it be counted with or not. It means "counted" like omit (if it false), not "enumerabled" like ascending or descending.
A answere like that is not a good answer: Please use only letters [a-zA-Z] or leastwise a letter at the first position of keyword.
What I want: That the keys are not sorted, but output in the order in which they were entered, whether integer, string or symbol.
Disclaimer: Please solutions only in JavaScript.
Javascript Objects are unordered by their nature. If you need an ordered object-like variable I would suggest using a map.
To achieve what you're looking for with a map instead of object you'd do something like the below:
var map1 = new Map();
map1.set("123", "c");
map1.set(123, "b");
var iterator1 = map1.keys();
var myarray = [];
for (var i = 0; i < map1.size; i++) {
myarray.push(iterator1.next().value);
}
console.log(myarray);
// Array ["123", 123]
Unfortunately it's not compatible with IE and I'm not sure how else you could achieve what you need without it. A quick Google did return something about jQuery maps, though.
If you don't want to use jQuery and still need to support IE some points are below:
Is there anything stopping you using an array rather than JS object to store the data you need? This will retain the order per your requirements unlike objects. You could have an object entry in each iteration which represents the key then use a traditional foreach to obtain them as an array. I.e.
The array:
var test_array = [
{key: 123, value: 'a value here'},
{key: "123", value: 'another value here'}
];
// console.log(test_array);
Getting the keys:
var test_array_keys = [];
test_array.forEach(function(obj) { test_array_keys.push(obj['key']); } );
// console.log(test_array_keys);
Then if you needed to check whether the key exists before adding a new entry (to prevent duplicates) you could do:
function key_exists(key, array)
{
return array.indexOf(key) !== -1;
}
if(key_exists('12345', test_array_keys))
{
// won't get here, this is just for example
console.log('Key 12345 exists in array');
}
else if(key_exists('123', test_array_keys))
{
console.log('Key 123 exists in array');
}
Would that work? If not then the only other suggestion would be keeping a separate array alongside the object which tracks the keys and is updated when an entry is added or removed to/from the object.
Object Keys sorted and store in array
First Creating student Object. then sort by key in object,last keys to store in array
const student={tamil:100, english:55, sci:85,soc:57}
const sortobj =Object.fromEntries(Object.entries(student).sort())
console.log(Object.keys(sortobj))
use map instead of an object.
let map = new Map()
map.set("a", 5)
map.set("d", 6)
map.set("b", 12)
to sort the keys (for example, to update a chart data)
let newMap = new Map([...map.entries()].sort())
let keys = Array.from(newMap.keys()) // ['a','b','d']
let values = Array.from(newMap.values()) // [5,12,6]
Let's say I have an array of strings and I want to get a list with objects that match, such as:
var locales=Locale.getAvailableLocales()
val filtered = locales.filter { l-> l.language=="en" }
except, instead of a single value I want to compare it with another list, like:
val lang = listOf("en", "fr", "es")
How do I do that? I'm looking for a one-liner solution without any loops. Thanks!
Like this
var locales = Locale.getAvailableLocales()
val filtered = locales.filter { l -> lang.contains(l.language)}
As pointed out in comments, you can skip naming the parameter to the lambda, and use it keyword to have either of the following:
val filtered1 = locales.filter{ lang.contains(it.language) }
val filtered2 = locales.filter{ it.language in lang }
Just remember to have a suitable data structure for the languages, so that the contains() method has low time complexity like a Set.
I have two arrays:
GlobalArray:Array(Int,Array[String]) and SpecificArray:Array(Int,Int).
The first Int in both of them is a key and I would like to get the element corresponding to that key from the GlobalArray.
In pseudocode:
val v1
For each element of SpecificArray
Get the corresponding element from GlobalArray to use its Array[String]
If (sum <= 100)
for each String of the Array
update v1
// ... some calculation
sum += 1
println (v1)
I know using .map() I could go through each position of the SpecificArray, but so far I was able to do this:
SpecificArray.map{x => val in_global = GlobalArray.filter(e => (e._1 == x._1))
// I don't know how to follow
}
How about something like below, I would prefer to for comprehension code which has better readability.
var sum:Int = 0
var result:String = ""
for {
(k1,v1) <- SpecificArray //v1 is the second int from specific array
(k2,values) <- GlobalArray if k1 == k2 //values is the second array from global array
value <- values if sum < 100 //value is one value frome the values
_ = {sum+=1; result += s"(${v1}=${value})"} //Update something here with the v1 and value
} yield ()
println(result)
Note needs more optimization
Convert GlobalArray to Map for faster lookup.
val GlobalMap = GlobalArray.toMap
SpecificArray.flatMap(x => GlobalMap(x._1))
.foldLeft(0)((sum:Int, s:String) => {
if(sum<=100) {
// update v1
// some calculation
}
sum+1
})
If not all keys of SpecificArray is present in GlobalMap then use GlobalMap.getOrElse(x._1, Array())
How sum affects the logic and what exactly is v1 is not clear from your code, but it looks like you do search through GlobalArray many times. If this is so, it makes sense to convert this array into a more search-friendly data structure: Map. You can do it like this
val globalMap = GlobalArray.toMap
and then you may use to join the strings like this
println(SpecificArray.flatMap({case (k,v) => globalMap(k).map(s => (k,v,s))}).toList)
If all you need is strings you may use just
println(SpecificArray.flatMap({case (k,v) => globalMap(k)}).toList)
Note that this code assumes that for every key in the SpecificArray there will be a matching key in the GlobalArray. If this is not the case, you should use some other method to access the Map like getOrElse:
println(SpecificArray.flatMap({case (k,v) => globalMap.getOrElse(k, Array()).map(s => (k,v,s))}).toList)
Update
If sum is actually count and it works for whole "joined" data rather than for each key in the SpecificArray, you may use take instead of it. Code would go like this:
val joined = SpecificArray2.flatMap({case (k,v) => globalMap.getOrElse(k, Array()).map(s => (s,v))})
.take(100) // use take instead of sum
And then you may use joined whatever way you want. And updated demo that builds v1 as joined string of form v1 += String_of_GlobalArray + " = " + 2nd_Int_of_SpecificArray is here. The idea is to use mkString instead of explicit variable update.
Goal: I have two different classes, and two arrays containing members of each class. Using Swift 2.0, I would like to find the unique members of one array compared to the other based on specific attributes of each class.
Example:
class A {
var name: String
init(name: String) {
self.name = name
}
}
class B {
var title: String
init(title: String) {
self.title = title
}
}
let aArray = [A(name:"1"), A(name:"2"), A(name:"3"), A(name:"4")]
let bArray = [B(title:"1"), B(title:"2"), B(title:"5")]
So, I'm looking for some operation between aArray and bArray which returns the 3rd and 4th element of aArray, because they are uniquely in aArray, where the basis of comparison is the attributes A.name and B.title.
Of course, reversing the order of the operation would pick out the 3rd element of bArray, because it is uniquely in bArray.
I know I can accomplish the goal straightforwardly using a simple for loop, but I was hoping for something more elegant and more optimized. But if a for loop is as fast or faster than anything fancier, I'm happy to use it just as well.
I'm not sure fancy or elegant this code is, but, we could do something like this:
let mappedArray = bArray.map { $0.title }
let filteredArray = aArray.filter { !mappedArray.contains($0.name) }
So when we want the unique elements from aArray, we first map the elements from bArray to get an array of the value we want to actually compare:
let mappedArray = bArray.map { $0.title }
mappedArray is just an array of strings based on the title property of the objects in bArray.
Next, we use the filter method to filter objects from aArray. The filter method returns an array with objects that pass the test in our closure. The test we want to apply is objects that are not contained in the mapped array we just built.
let filteredArray = aArray.filter { !mappedArray.contains($0.name) }
If we want to do it the other way, just change a few things:
let mappedArray = aArray.map { $0.name }
let filteredArray = bArray.filter { !mappedArray.contains($0.title) }