Related
I need a smart solution for such situation. I have a list of values such as:
{'a', 'b', 'abc', 'd', 'e', 'abc'}
As you can see, the string 'abc' comes twice. I need to delete all elements before first 'abc' and all elements after last 'abc'.
local list = {'a', 'b', 'abc', 'd', 'e', 'abc'}
local doubled_value = get_doubled_value (list) -- returns 'abc'
for i = 1, #list do
if not (list[1] == doubled_value) then
table.remove(list, 1)
else
break
end
end
table.remove is very slow, the easiest way is to create an index for values and make new list:
<script src="https://github.com/fengari-lua/fengari-web/releases/download/v0.1.4/fengari-web.js"></script>
<script type="application/lua">
function doubled_list(list)
local index = {}
for i,n in ipairs(list) do
if index[n] then
local newlist = {}
for j = index[n],i do
newlist[#newlist + 1] = list[j]
end
return newlist
end
index[n] = i
end
return list
end
local list = doubled_list({'a', 'b', 'abc', 'd', 'e', 'abc', 'f', 'g'})
print(table.concat(list,','))
</script>
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
There's two arrays of hash and I want remove the 'common' elements from the two arrays, based on certain keys. For example:
array1 = [{a: '1', b:'2', c:'3'}, {a: '4', b: '5', c:'6'}]
array2 = [{a: '1', b:'2', c:'10'}, {a: '3', b: '5', c:'6'}]
and the criteria keys are a and b. So when I get the result of something like
array1-array2 (don't have to overwrite '-' if there's better approach)
it will expect to get
[{a: '4', b: '5', c:'6'}]
sine we were using a and b as the comparing criteria. It will wipe the second element out since the value for a is different for array1.last and array2.last.
As I understand, you are given two arrays of hashes and a set of keys. You want to reject all elements (hashes) of the first array whose values match the values of any element (hash) of the second array, for all specified keys. You can do that as follows.
Code
require 'set'
def reject_partial_dups(array1, array2, keys)
set2 = array2.each_with_object(Set.new) do |h,s|
s << h.values_at(*keys) if (keys-h.keys).empty?
end
array1.reject do |h|
(keys-h.keys).empty? && set2.include?(h.values_at(*keys))
end
end
The line:
(keys-h.keys).empty? && set2.include?(h.values_at(*keys))
can be simplified to:
set2.include?(h.values_at(*keys))
if none of the values of keys in the elements (hashes) of array1 are nil. I created a set (rather than an array) from array2 in order to speed the lookup of h.values_at(*keys) in that line.
Example
keys = [:a, :b]
array1 = [{a: '1', b:'2', c:'3'}, {a: '4', b: '5', c:'6'}, {a: 1, c: 4}]
array2 = [{a: '1', b:'2', c:'10'}, {a: '3', b: '5', c:'6'}]
reject_partial_dups(array1, array2, keys)
#=> [{:a=>"4", :b=>"5", :c=>"6"}, {:a=>1, :c=>4}]
Explanation
First create set2
e0 = array2.each_with_object(Set.new)
#=> #<Enumerator: [{:a=>"1", :b=>"2", :c=>"10"}, {:a=>"3", :b=>"5", :c=>"6"}]
# #:each_with_object(#<Set: {}>)>
Pass the first element of e0 and perform the block calculation.
h,s = e0.next
#=> [{:a=>"1", :b=>"2", :c=>"10"}, #<Set: {}>]
h #=> {:a=>"1", :b=>"2", :c=>"10"}
s #=> #<Set: {}>
(keys-h.keys).empty?
#=> ([:a,:b]-[:a,:b,:c]).empty? => [].empty? => true
so compute:
s << h.values_at(*keys)
#=> s << {:a=>"1", :b=>"2", :c=>"10"}.values_at(*[:a,:b] }
#=> s << ["1","2"] => #<Set: {["1", "2"]}>
Pass the second (last) element of e0 to the block:
h,s = e0.next
#=> [{:a=>"3", :b=>"5", :c=>"6"}, #<Set: {["1", "2"]}>]
(keys-h.keys).empty?
#=> true
so compute:
s << h.values_at(*keys)
#=> #<Set: {["1", "2"], ["3", "5"]}>
set2
#=> #<Set: {["1", "2"], ["3", "5"]}>
Reject elements from array1
We now iterate through array1, rejecting elements for which the block evaluates to true.
e1 = array1.reject
#=> #<Enumerator: [{:a=>"1", :b=>"2", :c=>"3"},
# {:a=>"4", :b=>"5", :c=>"6"}, {:a=>1, :c=>4}]:reject>
The first element of e1 is passed to the block:
h = e1.next
#=> {:a=>"1", :b=>"2", :c=>"3"}
a = (keys-h.keys).empty?
#=> ([:a,:b]-[:a,:b,:c]).empty? => true
b = set2.include?(h.values_at(*keys))
#=> set2.include?(["1","2"] => true
a && b
#=> true
so the first element of e1 is rejected. Next:
h = e1.next
#=> {:a=>"4", :b=>"5", :c=>"6"}
a = (keys-h.keys).empty?
#=> true
b = set2.include?(h.values_at(*keys))
#=> set2.include?(["4","5"] => false
a && b
#=> false
so the second element of e1 is not rejected. Lastly:
h = e1.next
#=> {:a=>1, :c=>4}
a = (keys-h.keys).empty?
#=> ([:a,:c]-[:a,:b]).empty? => [:c].empty? => false
so return true (meaning the last element of e1 is not rejected), as there is no need to compute:
b = set2.include?(h.values_at(*keys))
So you really should try this out yourself because I am basically solving it for you.
The general approach would be:
For every time in array1
Check to see the same value in array2 has any keys and values with the same value
If they do then, delete it
You would probably end up with something like array1.each_with_index { |h, i| h.delete_if {|k,v| array2[i].has_key?(k) && array2[i][k] == v } }
I've been playing around with arrays, and I don't understand where Nil is coming from and why the a[4] isn't being overwritten. Please see my example below.
a = Array.new
a[5] = '5';
a[0, 3] = 'a', 'b', 'c', 'd';
a[4] = 'hello'
template = ERB.new "<%= a %>"
puts template.result(binding)
Returns me the result
["a", "b", "c", "d", "hello", nil, "5"]
and
a = Array.new
a[4] = '5';
a[0, 3] = 'a', 'b', 'c', 'd';
a[4] = 'hello'
template = ERB.new "<%= a %>"
puts template.result(binding)
Returns me the result
["a", "b", "c", "d", "hello", "5"]
Thanks in advance for the help!
In Ruby when you say:
a[0, 3] = a, b, c
it means that starting from the index 0 start inserting 3 objects into the array. but if you say
a[0, 3] = a, b, c, d
since they are more than three elements, another element is inserted after the third object, therefore shifting 5 and its preceding nil to the next positions.
actually it's:
a[start, length]
not
a[start, end]
The problem (as Pedram explained while I typed this) is that a[0, 3] = 'a', 'b', 'c', 'd' does not replace values from a[0] to a[3]. It replaces 3 values beginning at a[0] (a[0], a[1], and a[2]), but since you are giving it 4 values, it inserts the fourth one as a new element after a[2].
If you want to use a range of indexes, use a[0..3]. Or you can use a[0, 4].
This is simple.
You are defining an Array which is empty at first. In Ruby, if you are inserting a value into an out-of-bound array it will fill all the other past indexes with nil.
For example, if I do:
a = []
a[9] = '10'
puts a
The output will be:
[nil, nil, nil, nil, nil, nil, nil, nil, nil, "10"]
All nine indexes before the one I inserted will be filled with nil. Which is the representation of nothing.
Suppose I have a node with a collection in a property, say
START x = node(17) SET x.c = [ 4, 6, 2, 3, 7, 9, 11 ];
and somewhere (i.e. from .csv file) I get another collection of values, say
c1 = [ 11, 4, 5, 8, 1, 9 ]
I'm treating my collections as just sets, order of elements does not matter. What I need is to merge x.c with c1 with come magic operation so that resulting x.c will contain only distinct elements from both. The following idea comes to mind (yet untested):
LOAD CSV FROM "file:///tmp/additives.csv" as row
START x=node(TOINT(row[0]))
MATCH c1 = [ elem IN SPLIT(row[1], ':') | TOINT(elem) ]
SET
x.c = [ newxc IN x.c + c1 WHERE (newx IN x.c AND newx IN c1) ];
This won't work, it will give an intersection but not a collection of distinct items.
More RTFM gives another idea: use REDUCE() ? but how?
How to extend Cypher with a new builtin function UNIQUE() which accept collection and return collection, cleaned form duplicates?
UPD. Seems that FILTER() function is something close but intersection again :(
x.c = FILTER( newxc IN x.c + c1 WHERE (newx IN x.c AND newx IN c1) )
WBR,
Andrii
How about something like this...
with [1,2,3] as a1
, [3,4,5] as a2
with a1 + a2 as all
unwind all as a
return collect(distinct a) as unique
Add two collections and return the collection of distinct elements.
dec 15, 2014 - here is an update to my answer...
I started with a node in the neo4j database...
//create a node in the DB with a collection of values on it
create (n:Node {name:"Node 01",values:[4,6,2,3,7,9,11]})
return n
I created a csv sample file with two columns...
Name,Coll
"Node 01","11,4,5,8,1,9"
I created a LOAD CSV statement...
LOAD CSV
WITH HEADERS FROM "file:///c:/Users/db/projects/coll-merge/load_csv_file.csv" as row
// find the matching node
MATCH (x:Node)
WHERE x.name = row.Name
// merge the collections
WITH x.values + split(row.Coll,',') AS combo, x
// process the individual values
UNWIND combo AS value
// use toInt as the values from the csv come in as string
// may be a better way around this but i am a little short on time
WITH toInt(value) AS value, x
// might as well sort 'em so they are all purdy
ORDER BY value
WITH collect(distinct value) AS values, x
SET x.values = values
You could use reduce like this:
with [1,2,3] as a, [3,4,5] as b
return reduce(r = [], x in a + b | case when x in r then r else r + [x] end)
Since Neo4j 3.0, with APOC Procedures you can easily solve this with apoc.coll.union(). In 3.1+ it's a function, and can be used like this:
...
WITH apoc.coll.union(list1, list2) as unionedList
...
Given:
a1 = [5, 1, 6, 14, 2, 8]
I would like to determine if it contains all elements of:
a2 = [2, 6, 15]
In this case the result is false.
Are there any built-in Ruby/Rails methods to identify such array inclusion?
One way to implement this is:
a2.index{ |x| !a1.include?(x) }.nil?
Is there a better, more readable, way?
a = [5, 1, 6, 14, 2, 8]
b = [2, 6, 15]
a - b
# => [5, 1, 14, 8]
b - a
# => [15]
(b - a).empty?
# => false
Perhaps this is easier to read:
a2.all? { |e| a1.include?(e) }
You can also use array intersection:
(a1 & a2).size == a1.size
Note that size is used here just for speed, you can also do (slower):
(a1 & a2) == a1
But I guess the first is more readable. These 3 are plain ruby (not rails).
This can be achieved by doing
(a2 & a1) == a2
This creates the intersection of both arrays, returning all elements from a2 which are also in a1. If the result is the same as a2, you can be sure you have all elements included in a1.
This approach only works if all elements in a2 are different from each other in the first place. If there are doubles, this approach fails. The one from Tempos still works then, so I wholeheartedly recommend his approach (also it's probably faster).
If there are are no duplicate elements or you don't care about them, then you can use the Set class:
a1 = Set.new [5, 1, 6, 14, 2, 8]
a2 = Set.new [2, 6, 15]
a1.subset?(a2)
=> false
Behind the scenes this uses
all? { |o| set.include?(o) }
You can monkey-patch the Array class:
class Array
def contains_all?(ary)
ary.uniq.all? { |x| count(x) >= ary.count(x) }
end
end
test
irb(main):131:0> %w[a b c c].contains_all? %w[a b c]
=> true
irb(main):132:0> %w[a b c c].contains_all? %w[a b c c]
=> true
irb(main):133:0> %w[a b c c].contains_all? %w[a b c c c]
=> false
irb(main):134:0> %w[a b c c].contains_all? %w[a]
=> true
irb(main):135:0> %w[a b c c].contains_all? %w[x]
=> false
irb(main):136:0> %w[a b c c].contains_all? %w[]
=> true
irb(main):137:0> %w[a b c d].contains_all? %w[d c h]
=> false
irb(main):138:0> %w[a b c d].contains_all? %w[d b c]
=> true
Of course the method can be written as a standard-alone method, eg
def contains_all?(a,b)
b.uniq.all? { |x| a.count(x) >= b.count(x) }
end
and you can invoke it like
contains_all?(%w[a b c c], %w[c c c])
Indeed, after profiling, the following version is much faster, and the code is shorter.
def contains_all?(a,b)
b.all? { |x| a.count(x) >= b.count(x) }
end
Most answers based on (a1 - a2) or (a1 & a2) would not work if there are duplicate elements in either array. I arrived here looking for a way to see if all letters of a word (split to an array) were part of a set of letters (for scrabble for example). None of these answers worked, but this one does:
def contains_all?(a1, a2)
try = a1.chars.all? do |letter|
a1.count(letter) <= a2.count(letter)
end
return try
end
Depending on how big your arrays are you might consider an efficient algorithm O(n log n)
def equal_a(a1, a2)
a1sorted = a1.sort
a2sorted = a2.sort
return false if a1.length != a2.length
0.upto(a1.length - 1) do
|i| return false if a1sorted[i] != a2sorted[i]
end
end
Sorting costs O(n log n) and checking each pair costs O(n) thus this algorithm is O(n log n). The other algorithms cannot be faster (asymptotically) using unsorted arrays.
I was directed to this post when trying to find whether one array ["a", "b", "c"] contained another array ["a", "b"], where in my case identical ordering was an additional requirement to the question.
Here is my solution (I believe it's O(n) complexity), to anyone who has that extra requirement:
def array_includes_array(array_to_inspect, array_to_search_for)
inspectLength = array_to_inspect.length
searchLength = array_to_search_for.length
if searchLength == 0 then
return true
end
if searchLength > inspectLength then
return false
end
buffer = []
for i in 0..inspectLength
buffer.push(array_to_inspect[i])
bufferLastIndex = buffer.length - 1
if(buffer[bufferLastIndex] != array_to_search_for[bufferLastIndex]) then
buffer.clear
next
end
if(buffer.length == searchLength) then
return true
end
end
return false
end
This produces the test results:
puts "1: #{array_includes_array(["a", "b", "c"], ["b", "c"])}" # true
puts "2: #{array_includes_array(["a", "b", "c"], ["a", "b"])}" # true
puts "3: #{array_includes_array(["a", "b", "c"], ["b", "b"])}" # false
puts "4: #{array_includes_array(["a", "b", "c"], ["c", "b", "a"])}" # false
puts "5: #{array_includes_array(["a", "b", "c"], [])}" # true
puts "6: #{array_includes_array([], ["a"])}" # false
puts "7: #{array_includes_array([], [])}" # true