Dual array correspondance - arrays

I just found myself in a position where I have two arrays in Tcl.
I'm given $W_Array and $P_Array.
I need to traverse through one array not knowing what the size of each one is before hand, and execute a command only when there is a value for both arrays. Yes the array lengths could be different.
What is the best way of doing this?

The other answers jumped to using lists, I presume you mean Tcl's array, which are also called hash maps or associative arrays.
I think you're asking for something like:
array set a1 {a 1 b 2 c 3 d 4 e 5}
array set a2 {z 0 x 1 b 2 e 99}
foreach n [array names a1] {
if {[info exists a2($n)]} {
puts "Do something with $a1($n) and $a2($n)"
}
}
# FOREACH LOOP RESULTS IN THESE TWO PRINTOUTS
Do something with 5 and 99
Do something with 2 and 2

Not sure exactly what you mean by "a value for both arrays", but tcl's foreach supports iteration over multiple arrays at once... so you can say e.g.
foreach w $W_Array p $P_Array {
if {$w == $val && $p == $val} {
...
}
}
When the arrays are not of the same length, foreach will return all values from the longest array and the empty value {} for the missing elements in any shorter arrays.

Use llength command to find out if the arrays contain a value.
if {[llength $W_Array] > 0 && [llength $P_Array] > 0} {
# Do something
}

Related

How to know if multiple arrays in a list are the same

I would like to know whether all arrays within a list are the same.
== compares two arrays, but I want to know if there is any library method to tell if all arrays within a list are the same.
You can traverse the list of arrays just once, comparing the first array with all the other arrays. If the first one is equal to all the others, then all arrays in the list are equal. Something like this will work:
arrays = [[1,3],[1,3],[1,3]]
array0 = arrays.first
arrays[1..-1].all? { |a| array0 == a }
# => true
arrays = [[1,3],[1,3],[1,4]]
array0 = arrays.first
arrays[1..-1].all? { |a| array0 == a }
# => false
I was curious about the performance of each of the solutions here. Please be welcome to edit this post with your own results, if you like.
In my tests, the difference between the approaches raised with the length of the list of arrays, so I preferably measured a long list of relatively short arrays. I always did a few runs to remove the possible influence of GC.
require 'benchmark'
n = 10
n_arrays = 1000000
arrays = [(1..n).to_a] * n_arrays
Benchmark.bm(14) do |bm|
bm.report("1st vs others:") do
array0 = arrays.first
arrays[1..-1].all? { |a| array0 == a }
end
bm.report("uniq:") { arrays.uniq.size == 1 }
bm.report("each_cons:") { arrays.each_cons(2).all?{|x, y| x == y} }
end
The results suggest that while the each_cons approach is about the same (only slightly slower) than the "1st vs others" approach, the one using uniq is much much slower.
user system total real
1st vs others: 0.080000 0.000000 0.080000 ( 0.080872)
uniq: 1.810000 0.000000 1.810000 ( 1.807646)
each_cons: 0.180000 0.000000 0.180000 ( 0.174251)
[[1,3],[1,3],[1,3]].uniq.size == 1
#=> true
[[1,3],[1,3],[1,4]].uniq.size == 1
#=> false
array.each_cons(2).all?{|x, y| x == y}

Ruby: Sorting an Array, skipping the first element

I want to sort an Array of Arrays of Strings by the first String skipping the first Array but I just don't have an idea how to do it using the build-in sort method. I could copy the whole array without the first element and sort the resutling Array then but isn't there a more elegant way to do this?
ar = [["zzzz", "skip", "this"], ["EFP3","eins","eins"], ["EFP10","zwei","zwei"], ["EFP1","drei","drei"]]
ar.sort!{ |a,b|
if a == ar.first # why doesn't
next # this
end # work ?
# compare length, otherwise it would be e.g. 10 < 3
if a[0].length == b[0].length
a[0] <=> b[0]
else
a[0].length <=> b[0].length
end
}
I want to have the result like this:
["zzzz", "skip", "this"], ["EFP1","drei","drei"], ["EFP3","eins","eins"], ["EFP10","zwei","zwei"]
sortet by "EFP#"
edit: I'm using Ruby 1.8, if it matters.
ar[1..-1].sort { whatever you want }
You can do it this way:
[ar.first] + ar[1..-1].sort{ |a,b| a[0] <=> b[0] }
# => [["zzzz", "skip", "this"], ["EFP1", "drei", "drei"], ["EFP10", "zwei", "zwei"], ["EFP3", "eins", "eins"]]
but isn't there a more elegant way to do this?
You could sort the other elements and re-assign them:
ar = [5, 4, 3, 2, 1]
ar[1..-1] = ar[1..-1].sort
ar #=> [5, 1, 2, 3, 4]
I want to have the result [...] sortet by "EFP#"
sort_by looks like the right tool:
ar = [["zzzz", "skip"], ["EFP3", "eins"], ["EFP10", "zwei"], ["EFP1", "drei"]]
ar[1..-1] = ar[1..-1].sort_by { |s, _| s[/\d+/].to_i }
ar #=> [["zzzz", "skip"], ["EFP1", "drei"], ["EFP3", "eins"], ["EFP10", "zwei"]]
s[/\d+/].to_i extracts the digits from s and converts it to an integer:
"EFP1"[/\d+/].to_i #=> 1
"EFP3"[/\d+/].to_i #=> 3
"EFP10"[/\d+/].to_i #=> 10
Others have explained how to get the right answer.
As for why it doesn't work, sort simply doesn't expect "next". "next" is a language construct intended for a normal loop. sort, however, is a function that repeatedly asks another function for a result. As a normal Ruby function, it can't detect if you returned "next" because that's the equivalent of returning nil (or leaving the body empty). And so, it can't have, and doesn't have, any conventions about how to handle a "next" instance.
It causes an error because nil is not a valid number to return from the |a,b| comparison.
The comparison returns -1, 0 or 1, so if you return 1 for the first it is sorted as the first element, as 1 it would become the last element.
ar.sort!{ |a,b|
if a == ar.first
-1
elsif a[0].length == b[0].length # compare length, otherwise it would be e.g. 10 < 3
a[0] <=> b[0]
else
a[0].length <=> b[0].length
end
}
#=>[["zzzz", "skip", "this"], ["EFP1", "drei", "drei"], ["EFP3", "eins", "eins"], ["EFP10", "zwei", "zwei"]]

Find a Duplicate in an array Ruby

I am trying to find the duplicate values in an array of strings between 1 to 1000000.
However, with the code I have, I get the output as all the entries that are doubled.
So for instance, if I have [1,2,3,4,3,4], it gives me the output of 3 4 3 4 instead of 3 4.
Here is my code:
array = [gets]
if array.uniq.length == array.length
puts "array does not contain duplicates"
else
puts "array does contain duplicates"
print array.select{ |x| array.count(x) > 1}
end
Also, every time I test my code, I have to define the array as array = [1,2,3,4,5,3,5]. The puts works but it does not print when I use array [gets].
Can someone help me how to fix these two problems?
How I wish we had a built-in method Array#difference:
class Array
def difference(other)
h = other.tally
reject { |e| h[e] > 0 && h[e] -= 1 }
end
end
though #user123's answer is more straightforward. (Array#difference is probably the more efficient of the two, as it avoids the repeated invocations of count.) See my answer here for a description of the method and links to its use.
In a nutshell, it differs from Array#- as illustrated in the following example:
a = [1,2,3,4,3,2,4,2]
b = [2,3,4,4,4]
a - b #=> [1]
a.difference b #=> [1, 3, 2, 2]
For the present problem, if:
arr = [1,2,3,4,3,4]
the duplicate elements are given by:
arr.difference(arr.uniq).uniq
#=> [3, 4]
For your first problem, you need to uniq function like
array.select{ |x| array.count(x) > 1}.uniq
For your second problem, when you receive a value using array = [gets] it would receive your entire sequence of array numbers as a single string, so everything would be stored in a[0] like ["1, 2 3 4\n"].
puts "Enter array"
array = gets.chomp.split(",").map(&:to_i)
if array.uniq.length == array.length
puts "array does not contain duplicates"
else
puts "array does contain duplicates"
print array.select{ |x| array.count(x) > 1}.uniq
end
copy this code in ruby file and try to run using
ruby file_name.rb
Coming to your 'gets' problem,
When you are doing a gets, your are basically getting a string as an input but not an array.
2.2.0 :001 > array = [gets]
1,2,1,4,1,2,3
=> ["1,2,1,4,1,2,3\n"]
See the above example, how the ruby interpreter took all your elements as a single string and put it in an array as a single array element. So you need to explicitly convert the input to an array with comma as a delimiter. The below will address both your questions.
array = gets.chomp
array = array.split(',').map(&:to_i)
if array.uniq.length == array.length
puts "array does not contain duplicates"
else
puts "array does contain duplicates"
print array.select{ |x| array.count(x) > 1}.uniq!
end

array of lists in r

Suppose if I want to have 10 element array each element is a list/map. I am doing this:
x = array(list(), 10)
x[1][[ "a" ]] = 1
Warning message:
In x[1][["a"]] = 1 :
number of items to replace is not a multiple of replacement length
>
Is this the right approach? I want each element of the array to be a map.
What you're calling an "array" is usually just called a list in R. You're getting tripped up by the difference between [ and [[ for lists. See the section "Recursive (list-like) objects" in help("[").
x[[1]][["a"]] <- 1
UPDATE:
Note that the solution above creates a list of named vectors. In other words, something like
x[[1]][["a"]] <- 1
x[[1]][["b"]] <- 1:2
won't work because you can't assign multiple values to one element of a vector. If you want to be able to assign a vector to a name, you can use a list of lists.
x[[1]] <- as.list(x[[1]])
x[[1]][["b"]] <- 1:2
If you really want to do this, then, because the elements of the lists in each element of the array do not have names, you can't index by a character vector. In your example, there is no x[1][[ "a" ]]:
> x[1][[ "a" ]]
NULL
If there are no names then you need to index by a numeric:
> x[1][[ 1 ]] <- 1
[1] 1
It would seem more logical to have a list though than an array:
> y <- vector(mode = "list", length = 10)
> y
[[1]]
NULL
[[2]]
NULL
[[3]]
NULL
[[4]]
NULL
[[5]]
NULL
....

How can I delete elements from a Perl array?

I have a pretty big array and I want to delete the 2nd, 8th, 14th etc. element from an array. My array currently is like this:
Element1 x A B C
Element 2 y A B C
Element 3 z A B C
Broadly, I want to delete the x, y and z (just as an example, my array is slighly more complex). And pull up the rest. As in, I don't want to have a blank space in their positions. I want to get:
Element 1 A B C
Element 2 A B C
Element 3 A B C
I tried to give this a try with my array "todelete":
print "#Before Deleting"; print
$todelete[0]; print "\n"; print
$todelete[2]; print "\n"; print
$todelete[3];
for ($count=2; $count<#todelete;
$count=$count+6) { delete
$todelete[$count]; }
print "#After Deleting"; print
$todelete[0]; print "\n"; print
$todelete[2]; print "\n"; print
$todelete[3];$todelete[3];
But, currently, I think it just unitializes my value, because when I print the result, it tells me:
Use of uninitialized value in print
Suggestions?
The function you want is splice.
delete $array[$index] is the same as calling $array[$index] = undef; it leaves a blank space in your array. For your specific problem, how about something like
#array = #array[ grep { $_ % 6 != 2 } 0 .. $#array ];
You can also use grep as a filter:
my $cnt = 0;
#todelete = grep { ++$cnt % 6 != 2 } #todelete;

Resources