search 2d javascript array - javascript

I've look at a few other posts but am still slightly confused about using arrays within arrays.
So I've created my array as below:
var pagesArray = [
{
category: 'pages1',
pages: [
'firstPage1',
'secondPage1',
'thirdPage1',
'fourthPage1'
]
}, {
category: 'pages2',
pages: [
'firstPage2',
'secondPage2',
'thirdPage2',
'fourthPage2'
]
}
];
Now I want to be able to search the array and find if a string exists in it. If it does, I want it to return the value of what position it is.
E.g.
jQuery.inArray('secondPage1', pagesArray)
to give the result:
pagesArray[0][1];
I'm not sure if I've written the array wrong and if I'm using inArray correctly. If I try to alert(pagesArray[0][1]); it gives a value of undefined. What am I doing wrong here?

Updated Demo
$.each(pagesArray, function (i, v) {
if((pos=$.inArray('secondPage1', pagesArray[i]['pages'])) !== -1){
console.log('pagesArray['+i+"]['pages']"+'['+pos+']');
};
});
output pagesArray[0]['pages'][1]
pagesArray[0] means found in first array [0] 0 means first
pagesArray[0]['pages'][1] found in first array [0] inside sub array ['pages'] at index [1] Second position as index starts from 0
Demo
$.each(pagesArray, function (i, v) {
if($.inArray('secondPage1', pagesArray[i]['pages']) !== -1){
alert('inarray');
};
});
to access category
console.log(pagesArray[1]['category']);
to access pages
console.log(pagesArray[1]['pages']);
to find values in array you can use
console.log($.inArray('secondPage1', pagesArray[0]['pages']));

Try this:
var pagesArray = [
{
category: 'pages1',
pages: [
'firstPage1',
'secondPage1',
'thirdPage1',
'fourthPage1' ]
}, {
category: 'pages2',
pages: [
'firstPage2',
'secondPage2',
'thirdPage2',
'fourthPage2' ]
}
];
for(var i = 0; i < pagesArray.length; i++)
{
var exist = jQuery.inArray("firstPage2", pagesArray[i]);
if(exist)
{
alert('Got the Value!!!!');
break;
}
}
Fiddle

function findInArray( arr, value ) {
var j;
for ( var i in arr ) {
if ( ~( j = arr[ i ].pages.indexOf( value ) ) )
return { i: +i, j: +j };
}
}
findInArray( pagesArray, 'thirdPage2' );
>> Object {i: 1, j: 2}
So it is pagesArray[ 1 ].pages[ 2 ]. Then:
var p = findInArray( pagesArray, 'thirdPage2' );
pagesArray[ p.i ].pages[ p.j ];
>> 'thirdPage2'
Accordingly you can write a method that returns the value or nothing to check whether an item exists:
function findInArray( arr, value ) {
var j;
for ( var i in arr ) {
if ( ~( j = arr[ i ].pages.indexOf( value ) ) )
return arr[ i ].pages[ j ];
}
return false;
}
findInArray( pagesArray, 'foo ' );
>> false
findInArray( pagesArray, 'fourthPage2' );
>> 'fourthPage2'

Related

Javascript length of nested lists

Goal: given an array of mixed types determine the number of elements at each level. If there are two sub-arrays at the same level, each of their elements count towards to the total number of elements at that level.
Approach:
Array.prototype.elementsAtLevels = function( level, levelData ) {
if ( level == undefined ) { level = 0; } else { level += 1 }
if ( levelData == undefined ) { levelData = {}; }
if ( levelData[level] == undefined ) { levelData[level] = this.length} else { levelData[level] += this.length }
this.map(function(e, i) {if (Array.isArray(e)){ e.elementsAtLevels(level, levelData) }})
return levelData
}
Test case:
[
1, // 0: 1
1, // 0: 2
1, // 0: 3
1, // 0: 4
[ // 0: 5
2, // 1: 1
2, // 1: 2
2 // 1: 3
],
[ // 0: 6
[ // 1: 4
3, // 2: 1
3 // 2: 2
],
[ // 1: 5
[ // 2: 3
4 // 3: 1
]
]
]
].elementsAtLevels()
// Object [ 6, 5, 3, 1 ]
Question:
Is there a more efficient way to calculate this?
I wrote something very similar to what you have, and in a very rudimentary benchmark, it ran in a little under half the time.
let a = [1,1,1,1,[2,2,2],[[3,3],[[4]]]];
Array.prototype.elementsAtLevels2 = function (level, lData) {
if (!level || !lData) {
level = 0;
lData = {};
}
if (!(level in lData)) {
lData[level] = this.length;
} else {
lData[level] += this.length;
}
this.forEach(function (v) {
if (Array.isArray(v))
v.elementsAtLevels2(level + 1, lData);
});
return lData;
}
console.log(a.elementsAtLevels2());
I'm guessing the main performance increase might be from the forEach vs map, map creates a new array, where forEach does not.
Edit
Here it is in JSBin
Here is my take on it. It resembles yours but it doesn't change the prototype and it uses an array instead of an object for return.
function arrayCounter(arr, level, levelData) {
if (level === void 0) {
level = 0;
}
if (levelData === void 0) {
levelData = [];
}
//Set default value for level
if (levelData[level] === void 0) {
levelData[level] = 0;
}
//Count length
levelData[level] += arr.length;
//Loop through list items
for (var i = 0; i < arr.length; i++) {
var value = arr[i];
//If array, perform a subcount
if (Array.isArray(value)) {
levelData = arrayCounter(value, level + 1, levelData);
}
}
return levelData;
}
//TEST
var data = [1, 1, 1, 1, [2, 2, 2],
[
[3, 3],
[
[4]
]
]
];
console.log(arrayCounter(data));
This thing performs about the same as yours, but at least it gives correct results:
function elementsAtLevel( array, result = [], level = 0 ){
result[level] = (result[level] || 0) + array.length
level++
for( const el of array ){
if( Array.isArray(el) )
elementsAtLevel(el, result, level)
}
return result
}
console.log( elementsAtLevel([1,1,1,1,[2,2,2],[[3,3],[[4]]]]) )
By correct I mean consistent: you counted subarrays as elements on the first level, but not any other.
Here's a prototype version:
Array.prototype.elementsAtLevel = function( result = [], level = 0 ){
result[level] = (result[level] || 0) + this.length
level++
for( const el of this ){
if( Array.isArray(el) )
el.elementsAtLevel(result, level)
}
return result
}
console.log( [1,1,1,1,[2,2,2],[[3,3],[[4]]]].elementsAtLevel() )
this recursive function should do the work
let arr = [1,1,1,1,[2,2,2],[[3,3],[[4]]]];
Array.prototype.elementsAtLevels = function(){
return this.reduce((acc, el, index, currentArray) => {
if(Array.isArray(el)){
return acc.concat(el.elementsAtLevels());
}
return [currentArray.length];
}, [])
}
console.log(arr.elementsAtLevels());

Pushing to array

I need to loop through array and each array in array that has extra values, push them to their parent array as separate item. I hope this makes sense..
This is the structure of my initial array:
{type:
[ 0:
value: "tomato"
],
[ 1:
{value: "apple",
[ extras:
[ 0: { value: "green" } ],
[ 1: { value: "red" } ]
]
],
[ 2:
value: "pineapple"
]
}
What the result would have to look like:
[type:
[ 0:
tomato
],
[ 1:
apple,
green,
red
],
[ 2:
pineapple
]
]
What I've tried and failed: (I also commented the error I get on right line)
var response = /* json of first codeblock in question is response from ajax */;
var items = JSON.parse( response );
var type = Object.keys( items )[0];
var myArray = []
var count = items[type].lenght;
//Loop through main items in "type"
for( i = 0; i < count; i++ ) {
var value = items[type][i][value];
myArray[type][i] = [value]; //Uncaught TypeError: Cannot set property '0' of undefined
if( items[type][i][extras] ) {
var extracount = items[type][i][extras].lenght;
//Loop through extras
for( k = 0; k < extracount; k++ ) {
var extra = items[type][i][extras][k][value];
myArray[type][i].push( extra );
}
}
}
My main problem that I don't understand and that seems to be the problem in my example as well:
If I declare an empty array, how do I:
push an item to that array also declaring a new array around that item?
push another item to that array that was made around the first item?
This is what I believe you want. The following code may be incorrect, because I'm approximating what I believe your items object contains.
var items = {
type: [
{
value: "tomato"
},
{
value: "apple",
extras: [
{
value: "green"
}, {
value: "red"
}
]
},
{
value: "pineapple"
}
]
};
var myArray = {
type: []
};
var count = items['type'].length;
//Loop through main items in "type"
for (i = 0; i < count; i++) {
var subarray = [];
subarray.push(items['type'][i]['value']);
if (items['type'][i]['extras']) {
var extracount = items['type'][i]['extras'].length;
//Loop through extras
for (k = 0; k < extracount; k++) {
var extra = items['type'][i]['extras'][k]['value'];
subarray.push(extra);
}
}
myArray['type'].push(subarray);
}
Some notes:
You will definitely need to learn the difference between an array and an object in javascript. There are plenty of resources online for this.
When retrieving/manipulating a property prop from an object obj (i.e. for a key-value pair), you will need to use obj.prop or obj['prop']. Note the use of a string in the latter example.
For an array arr, you should use arr.push(value) to push a new value onto the array.
Your problem is here:
var value = items[type][i][value];
you should change it to
var value = items[type][i].value;

How call back function of a function works

I have the following code
$.map( [ 0, 1, 2 ], function( n ) {
return n > 0 ? n + 1 : null;
});
Out Put: [ 2, 3 ]
I know $.map Translate all items in an array or object to new array of items.(Documentation).
What I want to know how call back function in a .map works(internal implentation)?
One possible answer could be
.map has some loop which passes each element of array to call back method, that return some value.
.map manage each value return from call back method.In this case push in some internal array.
At the end of loop .map return array.
EDIT
But I am not sure how it works, is it works as I explained??
But I am not sure is this how it works??
Yes, that's basically how it works. Full details, as always, in the source code (that line number will rot over time...). Currently, it looks like this:
map: function( elems, callback, arg ) {
var value,
i = 0,
length = elems.length,
isArray = isArraylike( elems ),
ret = [];
// Go through the array, translating each of the items to their new values
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback( elems[ i ], i, arg );
if ( value != null ) {
ret.push( value );
}
}
// Go through every key on the object,
} else {
for ( i in elems ) {
value = callback( elems[ i ], i, arg );
if ( value != null ) {
ret.push( value );
}
}
}
// Flatten any nested arrays
return concat.apply( [], ret );
},
Well, there is no better way to check than looking at the source code
function (elems, callback, arg) {
var value, i = 0,
length = elems.length,
isArray = isArraylike(elems),
ret = [];
// Go through the array, translating each of the items to their new values
if (isArray) {
for (; i < length; i++) {
value = callback(elems[i], i, arg);
if (value != null) {
ret.push(value);
}
}
// Go through every key on the object,
} else {
for (i in elems) {
value = callback(elems[i], i, arg);
if (value != null) {
ret.push(value);
}
}
}
// Flatten any nested arrays
return concat.apply([], ret);
}
Yes, whether its an Array version or object, it is looping and calling the callback to set the value
Yes, for both loops, the value is pushed
Yes, it is returning a flattened array by calling concat

Check if an object with index is in array

$.each(constructions, function(i,v) {
if ($.inArray(v.name, map[ii].buildings) == -1) {//stuff}
};
Where constructions is an array of objects, each with a unique name. map[ii].buildings is an array containing some of these objects. I want to iterate each object in constructions, checking if its name parameter appears in the objects of map[ii].buildings.
The above code works if the each element in the map[ii].buildings array is just the text string of the object name, but not if the element is the entire object.. close, but no dice >.<
Try using $.grep() instead of $.inArray(); you can specify a function to do the filtering for you.
Instead of checking for -1, you check whether the array that $.grep() returns has length == 0
Simple example: (would be easier if you posted the code / example of what "constructions" objects look like)
var constructions = [{
Name: "Mess hall",
SqFt: 5000
}, {
Name: "Infirmary",
SqFt: 2000
}, {
Name: "Bungalow",
SqFt: 2000
}, {
Name: "HQ",
SqFt: 2000
}];
var buildings = [{
Name: "Infirmary",
SqFt: 2000
}, {
Name: "HQ",
SqFt: 2000
}];
// found buildings will be list of items in "constructions" that is not in "buildings"
var foundBuildings = $.grep(constructions, function (constructionsItem) {
return $.grep(buildings, function (buildingsItem) {
return buildingsItem.Name === constructionsItem.Name
}).length == 0; // == 0 means "not in", and > 0 means "in"
});
// this just renders the results all pretty for ya
$.each(foundBuildings, function (idx, item) {
$("#output").append("<div>" + item.Name + "</div>");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='output'></div>
Example jsFiddle: http://jsfiddle.net/eLeuy9eg/3/
The non-jQuery way of doing this would be to use filter. Something like this:
// pass in an array and the key for which you want values
// it returns an array of those values
function getValues(arr, key) {
return arr.map(function (el) { return el[key]; });
}
function notFoundIn(arr, arr2) {
// grab the names of the buildings
var buildings = getValues(arr2, 'name');
// grab the names from the construction objects and filter
// those that are not in the building array
return getValues(arr, 'name').filter(function (el) {
return buildings.indexOf(el) === -1;
});
}
notFoundIn(constructions, buildings); // eg [ "one", "three" ]
DEMO
You could even add a new method to the array prototype. With this one you can use either simple arrays, or arrays of objects if you pass in a key. Note in this example I've replaced map and filter with loops that perform the same functions, but faster (see comments):
function getValues(arr, key) {
var out = [];
for (var i = 0, l = arr.length; i < l; i++) {
out.push(arr[i][key]);
}
return out;
}
if (!Array.prototype.notFoundIn) {
Array.prototype.notFoundIn = function (inThisArray, key) {
var thisArr = key ? getValues(this, key) : this;
var arrIn = key ? getValues(inThisArray, key) : inThisArray;
var out = [];
for (var i = 0, l = thisArr.length; i < l; i++) {
if (arrIn.indexOf(thisArr[i]) === -1) {
out.push(thisArr[i]);
}
}
return out;
}
}
constructions.notFoundIn(buildings, 'name');
[1, 2, 3].notFoundIn([2]); // [1, 3]
DEMO

Function to convert URL hash parameters into object (key value pairs)

Consider this string: #page?param1=a&param2=b&param3=c
A hybrid application I have been working on uses window.location.hash to route the application to the right page. Often, these URLs contain parameters after the hash. Sure, this isn't standard, but it's a good solution that works nicely for our application.
I need to create a function that will take all of the parameters in the hash and return them in a object, for example: {param: value}.
I have tried other questions solution's that involve window.location.search but sadly that just returns an empty string when the parameters are after a hash.
My attempt looks like this:
return JSON.parse('{"' + decodeURI(window.location.hash).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g,'":"') + '"}');
The solution is taken from another question that uses window.location.search but using window.location.hash doesn't quite work properly, the first parameter (after the question mark) shows as undefined.
How can I create a function that would return hash parameters in an object?
The desired result for the string above would be this:
{ param1: 'a', param2: 'b', param3: 'c' }
You can use this function:
function parseParms(str) {
var pieces = str.split("&"), data = {}, i, parts;
// process each query pair
for (i = 0; i < pieces.length; i++) {
parts = pieces[i].split("=");
if (parts.length < 2) {
parts.push("");
}
data[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]);
}
return data;
}
This is taken from the .parseParms() method of a larger set of functionality on github I wrote for parsing a URL into all its pieces.
Input is a string in the form of:
"aaa=1&bbb=99&name=Bob"
and it will return an object like this:
{aaa: 1, bbb: 99, name: "Bob"}
So, if you have other things in the string besides just the parameters above, then you would need to remove those first before calling this function.
Working demo:
function parseParms(str) {
var pieces = str.split("&"), data = {}, i, parts;
// process each query pair
for (i = 0; i < pieces.length; i++) {
parts = pieces[i].split("=");
if (parts.length < 2) {
parts.push("");
}
data[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]);
}
return data;
}
console.log(parseParms("aaa=1&bbb=99&name=Bob"));
the foreEach method on arrays makes it even shorter:
const result = {};
hash.split('&').forEach(item => {
result[item.split('=')[0]] = decodeURIComponent(item.split('=')[1]);
});
function parseParms(str)
{
var pieces = str.split( "&" ),
data = {},
i, parts, key;
// Process each query pair
for ( i = 0; i < pieces.length; i++ ) {
parts = pieces[i].split( "=" );
// No value, only key
if ( parts.length < 2 ) {
parts.push( "" );
}
key = decodeURIComponent( parts[ 0 ] );
value = decodeURIComponent( parts[ 1 ] );
// Key is an array
if ( key.indexOf( "[]" ) !== -1 ) {
key = key.substring( 0, key.indexOf( "[]" ) );
// Check already there
if ( "undefined" === typeof data[ key ] ) {
data[ key ] = [];
}
data[ key ].push( value );
} else {
data[ key ] = value;
}
}
return data;
}
Working example can be found here: https://jsbin.com/xitemecuvi/edit?js,console
Hope that helps.

Categories