Javascript, map returns undefined [duplicate] - javascript

This question already has answers here:
Why does JavaScript map function return undefined?
(13 answers)
Closed last month.
So I have 24 "person" objects, which I created using "names" array, so that "dude" names are repeating;
Next I made a function that compares some random numbers to object id-s, and if they are equal,then I am trying to make new array of person names that go with that id.
(I'm sorry if this sounds too complicated, but I really don't understand why my map method doesn't work.)
Here 2 versions of my code. The first one works, and does what I want.
var names = ["jim", "jack", "aaron", "hugh", "jeff", "cameron", "allen", "charlie"];
var len = 3,
arr1 = [],
counter = 1;
for (var i = 0; i < len; i++) {
names.forEach(name => {
arr1.push({
id: counter,
dude: name
});
counter++;
});
}
console.log(arr1);
function checkName(nums) {
var namesarr = [];
for (var i = 0; i < arr1.length; i++) {
nums.forEach(function(num) {
if (num === arr1[i].id) {
namesarr.push(arr1[i].dude);
}
});
}
return (namesarr);
};
console.log(checkName([1, 3, 6]));
But in second version, my map function returns undefined, and I really don't understand why?!
var names = ["jim", "jack", "aaron", "hugh", "jeff", "cameron", "allen", "charlie"];
var len = 3,
arr1 = [],
counter = 1;
for (var i = 0; i < len; i++) {
names.forEach(name => {
arr1.push({
id: counter,
dude: name
});
counter++;
});
}
console.log(arr1);
function checkName(nums) {
var namesarr;
for (var i = 0; i < arr1.length; i++) {
namesarr = nums.map(function(num) {
if (num === arr1[i].id) {
return arr1[i].dude;
}
});
}
return (namesarr);
};
console.log(checkName([1, 3, 6]));

You're overwriting the namesarr variable on each iteration of the loop. I think you meant to add to it on each pass, rather than overwrite it. To do that, you can use the array .concat method.
Then, finally, make sure to filter all the undefined values from the result before you return it.
var names = ["jim", "jack", "aaron", "hugh", "jeff", "cameron", "allen", "charlie"];
var len = 3,
arr1 = [],
counter = 1;
for (var i = 0; i < len; i++) {
names.forEach(name => {
arr1.push({
id: counter,
dude: name
});
counter++;
});
}
console.log(arr1);
function checkName(nums) {
var namesarr = [];
for (var i = 0; i < arr1.length; i++) {
namesarr = namesarr.concat(nums.map(function(num) {
if(num === arr1[i].id) {
return arr1[i].dude;
}
}));
}
return (namesarr.filter(Boolean));
};
console.log(checkName([1,3,6]));

The result of map() is an array of all the returned values. If a function doesn't execute a return statement, it's equivalent to ending with return undefined.
In your second version, whenever the if condition fails, you don't execute return arr1[i].dude;, so it's returning undefined by default. It's as if you'd written:
namesarr = nums.map(function(num) {
if (num === arr1[i].id) {
return arr1[i].dude;
} else {
return undefined;
}
});
The other difference between your two versions of the code is that the second version reassigns namesarr each time through the for loop. So you're just printing the result of the last iteration.
The first version assigns it once before the loop, and adds to it only when the if condition succeeds, so you get the elements from all iterations.

Your map function doesn't return something for all the items in the array (because of the if) so some values in the result array will be undefined.
Plus, your map is inside a loop that loops over arr1, so for each iteration of that loop, the array namesarr get overridden. So the map will be as if it was applied only for the last element in arr1, thus if nums contain N elements then namesarr will have at least N - 1 undefined values in it (N - 1 if the last object of arr1 matches nums, N if not).
The problem is better solved using reduce instead of map:
function checkName(nums) {
return arr1.reduce(function(namesarr, obj) { // for each object obj in the array arr1
if(nums.indexOf(obj.id) !== -1) { // if the object's id is in the array nums
namesarr.push(obj.dude); // then add the object's dude to the array namesarr
}
return namesarr;
}, []); // the empty array to initialize namesarr
}

Related

Javascript, comparing two arrays in order while skipping non-matching indexes

I have two arrays:
var arr1 = [1,2,3,4,5]
var arr2 = [7,1,8,2,12,3,4,28,5]
I need to go through arr2 looking for matches to arr1, but it has to be in order (1,2,3,4,5). As you can see in arr2, the order does exists, but there are some numbers in between.
[7,1,8,2,12,3,4,28,5]
I have about 50 arrays similar to arr2, so I need to look through each one, and when I find a match, push it out to a "results" object. Small issue though is that some arrays will not have the entire match, may only have 1,2,3 or any variation of the search. Also, if the array I'm searching in is NOT in order, (IE: starts at 2,3,4) skip over it entirely.
The idea is to loop through these arrays, and when I find a match, add a count to the results array.
For example, using arr1 as the search, go through these arrays:
[7,1,8,2,12,3,4,28,5],
[7,1,8,2,12,3,4],
[7,8,1,2],
[1,2,3]
and have a result that looks like this (a dictionary of what was searched for, and a count of what was found) :
{1:4, 2:4, 3:3, 4:2, 5:1}
I tried doing a bunch of for-loops, but I can't figure out how to skip over a number that I'm not looking for, and continue onto the next iteration, while saving the results into a dictionary object.
let list = [[7,1,8,2,12,3,4,28,5], [7,1,8,2,12,3,4], [7,8,1,2], [1,2,3]];
let search = [1, 2, 3, 4, 5];
// Initialize result with zeros:
let result = search.reduce((result, next) => {
result[next] = 0;
return result;
}, {});
// Increment result for items found:
list.forEach(array => {
for (let i = 0, j = 0; i < array.length && j < search.length; ++i) {
if (array[i] == search[j]) {
++result[search[j]];
++j;
}
}
});
console.log(result);
Essentially this:
var needle = [1,2,3,4,5]
var collection = [[7,1,8,2,12,3,4,28,5], [7,1,8,2,12,3,4], [7,8,1,2], [1,2,3]]
// start with an object
var results = {}
// populate object with zeros
needle.forEach(function (i) { results[i] = 0 })
// define an index to iterate through collection
var i = 0
// define an index to conditionally iterate through "arr1"
var j = 0
// define an index to iterate through collection arrays
var k = 0
// define surrogate for the arrays in the collection
var arr
while (i < collection.length) {
// get collection array
arr = collection[i]
// reset the indices
j = 0
k = 0
while (k < arr.length) {
// if same element on needle is in a collection array
if (needle[j] === arr[k]) {
// save it in an object starting at 1
results[needle[j]]++
j++ // increment needle
}
k++ // increment array in collection
}
i++ // increment collection
}
console.log(results) // {1:4, 2:4, 3:3, 4:2, 5:1}
I hope that helps!
var arr1 = [1,2,3,4,5];
var arr2 = [7,1,8,2,12,3,4,28,5];
function givenTwoArrays(a,b, obj){
var obj = obj || {};
var cond = true;
function otherMatch(indexFound,elementFound){
var indexOnA = a.indexOf(elementFound);
return a.some(function(ele, idx){
if(idx > indexOnA)
return b.some(function(bele,bidx){
return ele == bele && bidx < indexFound;
});
});
}
a.map(function(aele,idx){
if(cond){
var indexFound = b.findIndex(function(bele){
return aele == bele;
});
if(typeof indexFound !== 'undefined'){
if(!otherMatch(indexFound,aele)){
if(typeof obj[aele] !== 'undefined')
obj[aele]++;
else{
obj[aele] = 1;
}
} else {
cond = false;
}
}else
cond = false;
}
});
return obj;
}
console.log("first pass");
console.log(givenTwoArrays(arr1,arr2))
console.log("second pass");
console.log(givenTwoArrays(arr1,arr2,{
"1": 1,
"2": 1,
"3": 1,
"4": 1,
"5": 1
}));
I think this will work, just need to add a little recursion!
var orign = [1,2,3,4,5];
var arr = [[7,1,8,2,12,3,4,28,5], [7,1,8,2,12,3,4], [7,8,1,2], [1,2,3]];
//temp result
var arrTmp = [];
for (var x in arr){
var match = 0;
var mis = 1;
var curIndex = 0;
var cur = orign[curIndex];
var arrTmpX = [];
for(var y in arr[x]){
if(arr[x][y] !== cur){
mis=1;
}else{
//add match after mismatch
arrTmpX.push(cur);
curIndex++
cur = orign[curIndex];
}
}
arrTmp.push(arrTmpX);
}
//calc result
var result = {};
for (var x in orign){
result[orign[x]] = 0;
for(var y in arrTmp){
if(arrTmp[y].length>x)result[orign[x]]++;
}
}
console.log(result);
this works

Javascript: Write a function that takes in an array, and then returns an array with only unique numbers, only arrays removed

Write a function that takes in a list and returns a list with all of the duplicates removed (list will only have unique numbers).
Here's what I have so far:
var lista = [1,4,5,1,1,3,5,6,4,4,3];
function dupRemove (lista) {
//Sort the array in case it isn't sorted
lista.sort();
//Object to store duplicates and unique numbers
var listNumbers = {
"Duplicate Numbers": [],
"Unique Numbers": []
};
for (var i = 0; i < lista.length; i++) {
//check if it is not equal to the index of the array before it and after. if it isn't, that means its unique, push it in the uniques array.
if (lista[i] !== lista[i-1] && lista[i] !== lista[i+1]) {
listNumbers["Unique Numbers"].push(lista[i]);
} else {
listNumbers["Duplicate Numbers"].push(lista[i]);
}
}
return listNumbers;
}
Currently, my solution returns an object with keys with the values of "Duplicates": 1, 1, 1, 3, 3, 4, 4, 4, 5, 5 and "Uniques": 6.
How do I remove the duplicates from duplicates and then join these two keys into a single array?
Thank you.
that answer is seriously over -engineered- all you need to to is push all values into a new array if they are not already in it.
function=removeDups()
{
var lista = [1,4,5,1,1,3,5,6,4,4,3];
var uniqueValues=[];
var duplicateValues=[];
for(i=0;i<lista.length;i++)
{
if(uniqueValues.indexof(lista[i] == -1){uniqueValues.push(lista[i]}else{duplicateValues.push(lista[i]}
}
}
You could just use the default filter method that is on all Arrays
You don't need the sort function either. If the item is already found using the indexOf method it will not be added to the newly returned array created by the filter method
var list = [1,4,5,1,1,3,5,6,4,4,3];
function removeDup (arr) {
return arr.filter(function(item, pos) {
return arr.indexOf(item) == pos;
})
}
var sortedList = removeDup(list).sort(function(a,b){
return a - b
})
document.getElementsByTagName('div')[0].textContent = sortedList
<div></div>
Kind of a non elegant solution but it gives you the two arrays: one with the duplicate values and one with the unique ones. Since you cannot rely on .sort() you can just count things.
Function checkList will give you back those two arrays.
var list = [1,4,5,1,1,3,5,6,4,4,3];
console.log(checkList(list));
function checkList(list) {
var uniques = []; // will be [6]
var dups = []; // will be [1, 4, 5, 3]
var checked = []; // save what you have already checked so far
for(i = 0; i < list.length; i++) {
if(notChecked(list[i], checked)) {
checked.push(list[i]);
if(count(list[i], list) > 1) {
dups.push(list[i]);
} else {
uniques.push(list[i]);
}
}
}
return {dups: dups, uniques: uniques}
}
// count how many num in arr
function count(num, arr) {
var count = 0;
var i;
for(i = 0; i < arr.length; i++) {
if(arr[i] == num) count++;
if(count > 1) return count;
}
return count;
}
// check if num has not been checked
function notChecked(num, arr) {
return (arr.indexOf(num) == -1) ? true : false;
}

How to get unique values in an array [duplicate]

This question already has answers here:
Get all unique values in a JavaScript array (remove duplicates)
(91 answers)
Closed 1 year ago.
How can I get a list of unique values in an array? Do I always have to use a second array or is there something similar to java's hashmap in JavaScript?
I am going to be using JavaScript and jQuery only. No additional libraries can be used.
Here's a much cleaner solution for ES6 that I see isn't included here. It uses the Set and the spread operator: ...
var a = [1, 1, 2];
[... new Set(a)]
Which returns [1, 2]
Or for those looking for a one-liner (simple and functional) compatible with current browsers:
let a = ["1", "1", "2", "3", "3", "1"];
let unique = a.filter((item, i, ar) => ar.indexOf(item) === i);
console.log(unique);
Update 2021
I would recommend checking out Charles Clayton's answer, as of recent changes to JS there are even more concise ways to do this.
Update 18-04-2017
It appears as though 'Array.prototype.includes' now has widespread support in the latest versions of the mainline browsers (compatibility)
Update 29-07-2015:
There are plans in the works for browsers to support a standardized 'Array.prototype.includes' method, which although does not directly answer this question; is often related.
Usage:
["1", "1", "2", "3", "3", "1"].includes("2"); // true
Pollyfill (browser support, source from mozilla):
// https://tc39.github.io/ecma262/#sec-array.prototype.includes
if (!Array.prototype.includes) {
Object.defineProperty(Array.prototype, 'includes', {
value: function(searchElement, fromIndex) {
// 1. Let O be ? ToObject(this value).
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// 3. If len is 0, return false.
if (len === 0) {
return false;
}
// 4. Let n be ? ToInteger(fromIndex).
// (If fromIndex is undefined, this step produces the value 0.)
var n = fromIndex | 0;
// 5. If n ≥ 0, then
// a. Let k be n.
// 6. Else n < 0,
// a. Let k be len + n.
// b. If k < 0, let k be 0.
var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
// 7. Repeat, while k < len
while (k < len) {
// a. Let elementK be the result of ? Get(O, ! ToString(k)).
// b. If SameValueZero(searchElement, elementK) is true, return true.
// c. Increase k by 1.
// NOTE: === provides the correct "SameValueZero" comparison needed here.
if (o[k] === searchElement) {
return true;
}
k++;
}
// 8. Return false
return false;
}
});
}
Since I went on about it in the comments for #Rocket's answer, I may as well provide an example that uses no libraries. This requires two new prototype functions, contains and unique
Array.prototype.contains = function(v) {
for (var i = 0; i < this.length; i++) {
if (this[i] === v) return true;
}
return false;
};
Array.prototype.unique = function() {
var arr = [];
for (var i = 0; i < this.length; i++) {
if (!arr.contains(this[i])) {
arr.push(this[i]);
}
}
return arr;
}
var duplicates = [1, 3, 4, 2, 1, 2, 3, 8];
var uniques = duplicates.unique(); // result = [1,3,4,2,8]
console.log(uniques);
For more reliability, you can replace contains with MDN's indexOf shim and check if each element's indexOf is equal to -1: documentation
One Liner, Pure JavaScript
With ES6 syntax
list = list.filter((x, i, a) => a.indexOf(x) === i)
x --> item in array
i --> index of item
a --> array reference, (in this case "list")
With ES5 syntax
list = list.filter(function (x, i, a) {
return a.indexOf(x) === i;
});
Browser Compatibility: IE9+
Using EcmaScript 2016 you can simply do it like this.
var arr = ["a", "a", "b"];
var uniqueArray = Array.from(new Set(arr)); // Unique Array ['a', 'b'];
Sets are always unique, and using Array.from() you can convert a Set to an array. For reference have a look at the documentations.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
These days, you can use ES6's Set data type to convert your array to a unique Set. Then, if you need to use array methods, you can turn it back into an Array:
var arr = ["a", "a", "b"];
var uniqueSet = new Set(arr); // {"a", "b"}
var uniqueArr = Array.from(uniqueSet); // ["a", "b"]
//Then continue to use array methods:
uniqueArr.join(", "); // "a, b"
If you want to leave the original array intact,
you need a second array to contain the uniqe elements of the first-
Most browsers have Array.prototype.filter:
const unique = array1.filter((item, index, array) => array.indexOf(item) === index);
//if you need a 'shim':
Array.prototype.filter= Array.prototype.filter || function(fun, scope){
var T= this, A= [], i= 0, itm, L= T.length;
if(typeof fun== 'function'){
while(i<L){
if(i in T){
itm= T[i];
if(fun.call(scope, itm, i, T)) A[A.length]= itm;
}
++i;
}
}
return A;
}
Array.prototype.indexOf= Array.prototype.indexOf || function(what, i){
if(!i || typeof i!= 'number') i= 0;
var L= this.length;
while(i<L){
if(this[i]=== what) return i;
++i;
}
return -1;
}
Fast, compact, no nested loops, works with any object not just strings and numbers, takes a predicate, and only 5 lines of code!!
function findUnique(arr, predicate) {
var found = {};
arr.forEach(d => {
found[predicate(d)] = d;
});
return Object.keys(found).map(key => found[key]);
}
Example: To find unique items by type:
var things = [
{ name: 'charm', type: 'quark'},
{ name: 'strange', type: 'quark'},
{ name: 'proton', type: 'boson'},
];
var result = findUnique(things, d => d.type);
// [
// { name: 'charm', type: 'quark'},
// { name: 'proton', type: 'boson'}
// ]
If you want it to find the first unique item instead of the last add a found.hasOwnPropery() check in there.
Not native in Javascript, but plenty of libraries have this method.
Underscore.js's _.uniq(array) (link) works quite well (source).
If you don't need to worry so much about older browsers, this is exactly what Sets are designed for.
The Set object lets you store unique values of any type, whether
primitive values or object references.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
const set1 = new Set([1, 2, 3, 4, 5, 1]);
// returns Set(5) {1, 2, 3, 4, 5}
Using jQuery, here's an Array unique function I made:
Array.prototype.unique = function () {
var arr = this;
return $.grep(arr, function (v, i) {
return $.inArray(v, arr) === i;
});
}
console.log([1,2,3,1,2,3].unique()); // [1,2,3]
Short and sweet solution using second array;
var axes2=[1,4,5,2,3,1,2,3,4,5,1,3,4];
var distinct_axes2=[];
for(var i=0;i<axes2.length;i++)
{
var str=axes2[i];
if(distinct_axes2.indexOf(str)==-1)
{
distinct_axes2.push(str);
}
}
console.log("distinct_axes2 : "+distinct_axes2); // distinct_axes2 : 1,4,5,2,3
Majority of the solutions above have a high run time complexity.
Here is the solution that uses reduce and can do the job in O(n) time.
Array.prototype.unique = Array.prototype.unique || function() {
var arr = [];
this.reduce(function (hash, num) {
if(typeof hash[num] === 'undefined') {
hash[num] = 1;
arr.push(num);
}
return hash;
}, {});
return arr;
}
var myArr = [3,1,2,3,3,3];
console.log(myArr.unique()); //[3,1,2];
Note:
This solution is not dependent on reduce. The idea is to create an object map and push unique ones into the array.
You only need vanilla JS to find uniques with Array.some and Array.reduce. With ES2015 syntax it's only 62 characters.
a.reduce((c, v) => b.some(w => w === v) ? c : c.concat(v)), b)
Array.some and Array.reduce are supported in IE9+ and other browsers. Just change the fat arrow functions for regular functions to support in browsers that don't support ES2015 syntax.
var a = [1,2,3];
var b = [4,5,6];
// .reduce can return a subset or superset
var uniques = a.reduce(function(c, v){
// .some stops on the first time the function returns true
return (b.some(function(w){ return w === v; }) ?
// if there's a match, return the array "c"
c :
// if there's no match, then add to the end and return the entire array
c.concat(v)}),
// the second param in .reduce is the starting variable. This is will be "c" the first time it runs.
b);
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
Another thought of this question. Here is what I did to achieve this with fewer code.
var distinctMap = {};
var testArray = ['John', 'John', 'Jason', 'Jason'];
for (var i = 0; i < testArray.length; i++) {
var value = testArray[i];
distinctMap[value] = '';
};
var unique_values = Object.keys(distinctMap);
console.log(unique_values);
Array.prototype.unique = function () {
var dictionary = {};
var uniqueValues = [];
for (var i = 0; i < this.length; i++) {
if (dictionary[this[i]] == undefined){
dictionary[this[i]] = i;
uniqueValues.push(this[i]);
}
}
return uniqueValues;
}
I have tried this problem in pure JS.
I have followed following steps 1. Sort the given array, 2. loop through the sorted array, 3. Verify previous value and next value with current value
// JS
var inpArr = [1, 5, 5, 4, 3, 3, 2, 2, 2,2, 100, 100, -1];
//sort the given array
inpArr.sort(function(a, b){
return a-b;
});
var finalArr = [];
//loop through the inpArr
for(var i=0; i<inpArr.length; i++){
//check previous and next value
if(inpArr[i-1]!=inpArr[i] && inpArr[i] != inpArr[i+1]){
finalArr.push(inpArr[i]);
}
}
console.log(finalArr);
Demo
You can enter array with duplicates and below method will return array with unique elements.
function getUniqueArray(array){
var uniqueArray = [];
if (array.length > 0) {
uniqueArray[0] = array[0];
}
for(var i = 0; i < array.length; i++){
var isExist = false;
for(var j = 0; j < uniqueArray.length; j++){
if(array[i] == uniqueArray[j]){
isExist = true;
break;
}
else{
isExist = false;
}
}
if(isExist == false){
uniqueArray[uniqueArray.length] = array[i];
}
}
return uniqueArray;
}
Here is an approach with customizable equals function which can be used for primitives as well as for custom objects:
Array.prototype.pushUnique = function(element, equalsPredicate = (l, r) => l == r) {
let res = !this.find(item => equalsPredicate(item, element))
if(res){
this.push(element)
}
return res
}
usage:
//with custom equals for objects
myArrayWithObjects.pushUnique(myObject, (left, right) => left.id == right.id)
//with default equals for primitives
myArrayWithPrimitives.pushUnique(somePrimitive)
I was just thinking if we can use linear search to eliminate the duplicates:
JavaScript:
function getUniqueRadios() {
var x=document.getElementById("QnA");
var ansArray = new Array();
var prev;
for (var i=0;i<x.length;i++)
{
// Check for unique radio button group
if (x.elements[i].type == "radio")
{
// For the first element prev will be null, hence push it into array and set the prev var.
if (prev == null)
{
prev = x.elements[i].name;
ansArray.push(x.elements[i].name);
} else {
// We will only push the next radio element if its not identical to previous.
if (prev != x.elements[i].name)
{
prev = x.elements[i].name;
ansArray.push(x.elements[i].name);
}
}
}
}
alert(ansArray);
}
HTML:
<body>
<form name="QnA" action="" method='post' ">
<input type="radio" name="g1" value="ANSTYPE1"> good </input>
<input type="radio" name="g1" value="ANSTYPE2"> avg </input>
<input type="radio" name="g2" value="ANSTYPE3"> Type1 </input>
<input type="radio" name="g2" value="ANSTYPE2"> Type2 </input>
<input type="submit" value='SUBMIT' onClick="javascript:getUniqueRadios()"></input>
</form>
</body>

How do I slice an array from an array of object literals?

I have this array, in which each index contains an object literal. All of the object literals have the same properties. Some of the object literals have the same value for a given property, and I want to create a new array containing only those object literals.
My idea is to sort the array, and slice it into a new array...
Here is the array:
var arr = [];
arr[0] =
{
country: "United States",
num: 27
};
arr[1] =
{
country: "Australia",
num: 5
};
arr[2] =
{
country: "United States",
num: 7
};
So, I want to create a new array containing only those objects where the property country is "United States". This is my crazy idea so far, which doesn't work:
function getNewArray(arr)
{
var arr2 = [];
for(var key in arr)
{
for(var i = 0; i < arr.length - 1; i++)
{
if(arr.hasOwnProperty(key) && arr[i].name == arr[i + 1].name)
{
arr2[i] = arr.slice(key);
}
}
}
return arr2;
}
var arr3 = getNewArray(arr).sort();
"I want to create a new array containing only those objects where the property country is "United States""
This is exactly what the Array.filter() method is for:
var filteredArray = arr.filter(function(val, i, a) {
return val.country==="United States";
});
Note that the .filter() method isn't available in IE before version 9, but the MDN page I linked to above shows you exactly how to implement it so reading that page should in itself answer your question.
Note also that in the (non-working) code in the question, your two for loops are basically doing the same thing as each other because they're both iterating over arr, so it doesn't make sense to nest them like that. You shouldn't use a for..in loop on an array, but if you do the key values will be the numeric indexes, it doesn't somehow pick up the properties of the object stored at each index.
EDIT:
"Some of the object literals have the same value for a given property, and I want to create a new array containing only those object literals."
OK, re-reading this I guess you didn't really want to select elements by specifying a country, you wanted to select elements for any country that had duplicate entries? So if there were another three elements that all had "New Zealand" you'd want to select them in addition to the "United States" ones? If so, you could do something like this:
var countryCount = {},
i;
for (i = 0; i < arr.length; i++)
if (countryCount.hasOwnProperty(arr[i].country)
countryCount[arr[i].country]++;
else
countryCount[arr[i].country] = 1;
var filteredArr = arr.filter(function(val, i, a) {
return countryCount[val.country] > 1;
});
var getCountry = function (country) {
var out = [];
for (var i = 0, len = arr.length; i < len; i++)
if (arr[i].country === country) out.push(arr[i]);
return out;
};
Demo: http://jsfiddle.net/YwytD/1/
There is a simpler way of doing this, I think this is what you want
var new_arr = arr.filter(function(obj){ return obj['country'] === 'United States'; })
This will filter your results into new_arr
Of course you can make it better and more generic than just 'United States'
Edit:
Whoops, got your question just now
Answer: Nested Filters :)
function getKeyStuff(key) {
return arr.filter( function(obj) {
var new_arr = arr.filter( function(inner_obj) {
return inner_obj[key] === obj[key];
});
return new_arr.length > 1;
});
}
Here is my solution:
// assuming arr is already set
var num_obj = arr.length;
var obj_by_country = {}, obj;
for (var i = 0; i < num_obj; i++) {
obj = arr[i];
if (!obj_by_country[obj.country]) {
obj_by_country[obj.country] = [];
}
obj_by_country[obj.country].push(obj);
}
// build final array
var final_array = [];
for (i in obj_by_country) {
if (obj_by_country[i].length > 1) {
final_array.push(obj_by_country[i]);
}
}
Can you use JQuery?
var arr = [];
arr[0] = { country: "United States", num: 27 };
arr[1] = { country: "Australia", num: 5 };
arr[2] = { country: "United States", num: 7 };
var newArray = [];
$.each(arr, function(){
if(this.country == "United States")
newArray.push(this);
});
getByKey = function(arr, key, value) {
var results = [];
for(var i = 0; i < arr.length; i++) {
if(arr[i][key] === value) {
results.push(arr[i]);
}
}
return results
}
here's a working example
http://jsfiddle.net/michaelghayes/UyHYz/2/

Shuffling array properties in JavaScript

I have a data dictionary like this:
var data = {
'text1': 1,
'text2': 2,
'text3': 3,
...
'text20': 20
];
I need to pick a random selection of those keys and then shuffle it's values. In the example, it should write something like this:
> console.log(choose(data, 5));
[ { key: 'text15', value: 8 },
{ key: 'text6', value: 3 },
{ key: 'text3', value: 15 },
{ key: 'text19', value: 6 },
{ key: 'text8', value: 19 } ]
For now I'm extracting the keys into another array and sorting by Math.random() but I'm stuck at swaping the values because no key should have the same value it initially had.
How would you swap key/values here?
Thanks
I put together a possible solution using underscore.js to simplify traversing the object and arrays in a cross browser manner:
var data = {
text1: 1,
text2: 2,
text3: 3,
text4: 4,
text5: 5,
text6: 6,
text7: 7,
text8: 8,
text9: 9,
text10: 10
};
function choose(data, num)
{
var keys = _.sortBy(
_.keys(data),
function(k)
{
return (Math.random() * 3) - 1;
}
),
results = [],
k1, k2;
if (num > keys.length) {
throw new Error('Impossible to retrieve more values than exist');
}
while (results.length < num) {
k1 = k2 || keys.pop();
k2 = keys.pop();
results.push({key:k1, value: data[k2]});
}
return results;
}
console.log(choose(data, 5));
This isn't necessarily an optimal approach but it seems to meet your requirements. I first grab all of the keys and sort them randomly. I then loop through the random keys creating a new object with one key and the following keys value. That way you'll always end up with a different value associated with each key. If you need it to work when the value of num passed in to the function == the number of keys in the data then you'll have to add a little more code - I'll leave that as an exercise for the reader :)
You can have a play with this code on jsfiddle:
http://jsfiddle.net/zVyQW/1/
You could do this:
collect names and corresponding values in two arrays names and values
shuffle both arrays independently of each other
take the first n items of both arrays and combine them
Here’s an example implementation:
Array.prototype.shuffle = function() {
for (var i=this.length-1, j, tmp; i>0; i--) {
j = Math.round(Math.random()*i);
tmp = this[i], this[i] = this[j], this[j] = tmp;
}
return this;
};
function choose(data, number) {
var names = [], values = [], pick = [];
for (var name in data) {
if (data.hasOwnProperty(name)) {
names.push(name);
values.push(data[name]);
}
}
names = names.shuffle(), values = values.shuffle();
for (var i=Math.min(number >>> 0, names.length-1); i>=0; i--) {
pick.push({key: names[i], value: values[i]});
}
return pick;
}
Been a while since this was answered, but I was working on shuffling and found the following to be by far the fastest implementation with an evenly random distribution.
It's fast because it only makes one call to Math.random on each iteration, all the rest is done by property access. It doesn't modify the array, just reassigns values.
function shuffle(a) {
var t, j, i=a.length, rand=Math.random;
// For each element in the array, swap it with a random
// element (which might be itself)
while (i--) {
k = rand()*(i+1)|0;
t = a[k];
a[k]=a[i];
a[i]=t;
}
return a;
}
It uses a combination of three functions (including the Array shuffle prototype method).
Here is the complete code:
var obj = {
"red":"RED",
"blue":"BLUE",
"green":"GREEN",
"yellow":"YELLOW",
"purple":"PURPLE"
};
Array.prototype.shuffle = function(){
for (var i = 0; i < this.length; i++){
var a = this[i];
var b = Math.floor(Math.random() * this.length);
this[i] = this[b];
this[b] = a;
}
}
obj = shuffleProperties(obj); // run shuffle
function shuffleProperties(obj) {
var new_obj = {};
var keys = getKeys(obj);
keys.shuffle();
for (var key in keys){
if (key == "shuffle") continue; // skip our prototype method
new_obj[keys[key]] = obj[keys[key]];
}
return new_obj;
}
function getKeys(obj){
var arr = new Array();
for (var key in obj)
arr.push(key);
return arr;
}
for(key in obj){
alert(key);
}
Check all post,
Best Regards.
Use an implementation of random that randomizes a discrete set of values, such as Math.rand seen here. For each index, randomize Math.rand(index, length-1) to get a list of random indexes, the location off all indices will change.

Categories

Resources